Repository: ansible/ansible

Issue:
Add "descr" parameter to aci_static_binding_to_epg - Bind static paths to EPGs (fv:RsPathAtt)
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
<!--- Explain the problem briefly -->
The aci_static_binding_to_epg module does not support the "descr" parameter (fv:RsPathAtt.descr)

##### ISSUE TYPE
 - Feature Idea
Add "descr" as an available parameter to set, change or filter on.

##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
aci_static_binding_to_epg

##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
~/ansible$ ansible --version
ansible 2.5.5
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/dan/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/dan/ansible/venv/local/lib/python2.7/site-packages/ansible
  executable location = /home/dan/ansible/venv/bin/ansible
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]

```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).-->

(venv) dan@bo-tst-ub-srv1:~/ansible$ ansible-config dump --only-changed
(venv) dan@bo-tst-ub-srv1:~/ansible$

q
##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->

 Running Ansible From: Ubuntu 16.04.4 LTS
OS Managing: Cisco ACI 2.1.3p

##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->



<!--- Paste example playbooks or commands between quotes below -->
```yaml

- name: Playbook to create a static path for a physical interface to an EPG
  hosts: lab-apic
  connection: local
  gather_facts: no

  vars:
    vOutputLevel: "debug"
    vTenant: "P4A"
    vAp: "WebApp1"
    vEPG: "Web"
    vEncapId: "1040"
    vDeployImmediacy: "lazy"
    vIntMode: "802.1p"
    vIntType: "switch_port"
    vPodId: "1"
    vLeafs: "101"
    vInterface: "1/8"
    #vDescr: "SERVER-E1"

  tasks:
    - name: Create Static Path (fvRsPathAtt) to EPG (fvAEPg)
      aci_static_binding_to_epg:
        host: "{{ ansible_host }}"
        username: "{{ ansible_user }}"
        private_key: "{{ ansible_private_key }}"
        validate_certs: no
        tenant: "{{vTenant}}"
        ap: "{{vAp}}"
        epg: "{{vEPG}}"
        encap_id: "{{vEncapId}}"
        deploy_immediacy: "{{vDeployImmediacy}}"
        interface_type: "{{vIntType}}"
        interface_mode: "{{vIntMode}}"
        pod_id: "{{vPodId}}"
        leafs: "{{vLeafs}}"
        interface: "{{vInterface}}"
        output_level: "{{ vOutputLevel }}"
        #descr: "{{vDescr}}"
        state: absent
      register: vResponse
    - debug: var=vResponse
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Expected results would be ability to set description and use that field for filtering.

The name can be useful when searching for a physical severs static path EPG bindings. For example, the following would only static bindings where fvRsPathAtt.descr contained TLOINF

# Extract using fvRsPathAtt.descr
moquery -c fvTenant -d uni/tn-TST -o xml -a config -x rsp-subtree=full rsp-subtree-class=fvAp,fvAEPg rsp-subtree-class=fvRsPathAtt rsp-subtree-filter='wcard(fvRsPathAtt.descr,"TLOINF")' rsp-prop-include=config-only

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
Not able to set "descr" parameter

<!--- Paste verbatim command output between quotes below -->
```

```



Discussion:
Files identified in the description:
* [lib/ansible/modules/network/aci/aci_static_binding_to_epg.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/aci/aci_static_binding_to_epg.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @brunocalogero @dagwieers @jmcgill298 @schunduri
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/network/aci/aci_static_binding_to_epg.py b/lib/ansible/modules/network/aci/aci_static_binding_to_epg.py
--- a/lib/ansible/modules/network/aci/aci_static_binding_to_epg.py
+++ b/lib/ansible/modules/network/aci/aci_static_binding_to_epg.py
@@ -38,6 +38,11 @@
     description:
     - The name of the end point group.
     aliases: [ epg_name ]
+  description:
+    description:
+    - Description for the static path to EPG binding.
+    aliases: [ descr ]
+    version_added: '2.7'
   encap_id:
     description:
     - The encapsulation ID associating the C(epg) with the interface path.
@@ -263,6 +268,7 @@ def main():
         tenant=dict(type='str', aliases=['tenant_name']),  # Not required for querying all objects
         ap=dict(type='str', aliases=['app_profile', 'app_profile_name']),  # Not required for querying all objects
         epg=dict(type='str', aliases=['epg_name']),  # Not required for querying all objects
+        description=dict(type='str', aliases=['descr']),
         encap_id=dict(type='int', aliases=['vlan', 'vlan_id']),
         primary_encap_id=dict(type='int', aliases=['primary_vlan', 'primary_vlan_id']),
         deploy_immediacy=dict(type='str', choices=['immediate', 'lazy']),
@@ -289,6 +295,7 @@ def main():
     tenant = module.params['tenant']
     ap = module.params['ap']
     epg = module.params['epg']
+    description = module.params['description']
     encap_id = module.params['encap_id']
     primary_encap_id = module.params['primary_encap_id']
     deploy_immediacy = module.params['deploy_immediacy']
@@ -383,6 +390,7 @@ def main():
         aci.payload(
             aci_class='fvRsPathAtt',
             class_config=dict(
+                descr=description,
                 encap=encap_id,
                 primaryEncap=primary_encap_id,
                 instrImedcy=deploy_immediacy,


Test patch:
diff --git a/test/integration/targets/aci_static_binding_to_epg/tasks/main.yml b/test/integration/targets/aci_static_binding_to_epg/tasks/main.yml
--- a/test/integration/targets/aci_static_binding_to_epg/tasks/main.yml
+++ b/test/integration/targets/aci_static_binding_to_epg/tasks/main.yml
@@ -8,7 +8,7 @@
     msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.'
   when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined
 
-- name: ensure static path to epg is deleted for test kick off
+- name: Ensure static path to epg is deleted for test kick off
   aci_static_binding_to_epg: &aci_static_binding_to_epg_absent
     host: '{{ aci_hostname }}'
     username: '{{ aci_username }}'
@@ -26,7 +26,7 @@
     interface: '1/7'
     state: absent
 
-- name: ensure tenant exists for tests to kick off
+- name: Ensure tenant exists for tests to kick off
   aci_tenant: &aci_tenant_present
     host: "{{ aci_hostname }}"
     username: "{{ aci_username }}"
@@ -35,23 +35,23 @@
     use_ssl: '{{ aci_use_ssl | default(true) }}'
     use_proxy: '{{ aci_use_proxy | default(true) }}'
     output_level: debug
-    state: present
     tenant: anstest
+    state: present
   register: tenant_present
 
-- name: ensure ap exists
+- name: Ensure ap exists
   aci_ap: &aci_ap_present
     <<: *aci_tenant_present
     ap: anstest
   register: ap_present
 
-- name: ensure epg exists
+- name: Ensure epg exists
   aci_epg: &aci_epg_present
     <<: *aci_ap_present
     epg: anstest
   register: epg_present
 
-- name: bind static-binding to epg - check mode works
+- name: Bind static-binding to epg - check mode works
   aci_static_binding_to_epg: &aci_static_binding_to_epg_present
     <<: *aci_epg_present
     encap_id: 222
@@ -64,30 +64,54 @@
   check_mode: yes
   register: provide_present_check_mode
 
-- name: bind static-binding to epg - provide works (creation w/o check-mode)
+- name: Bind static-binding to epg - provide works (creation w/o check-mode)
   aci_static_binding_to_epg:
     <<: *aci_static_binding_to_epg_present
   ignore_errors: yes
   register: provide_present
 
-- name: bind static-binding to epg - primary_encap_id works
+- name: Bind static-binding to epg - primary_encap_id works
   aci_static_binding_to_epg: &primary_encap_id_present
     <<: *aci_static_binding_to_epg_present
     primary_encap_id: 50
   register: primary_ecap_id_present
 
-- name: bind contract to epg - idempotency works again
+- name: Bind contract to epg - idempotency works again
   aci_static_binding_to_epg:
     <<: *primary_encap_id_present
   register: idempotent_present
 
-- name: missing required param - failure message works
+- name: Bind contract to epg - update description (check mode)
+  aci_static_binding_to_epg:
+    <<: *primary_encap_id_present
+    description: Binding description
+  register: description_cm
+
+- name: Bind contract to epg - update description (run mode)
+  aci_static_binding_to_epg:
+    <<: *primary_encap_id_present
+    description: Binding description
+  register: description
+
+- name: Bind contract to epg - update description (check mode)
+  aci_static_binding_to_epg:
+    <<: *primary_encap_id_present
+    description: Binding description
+  register: idempotent_description_cm
+
+- name: Bind contract to epg - update description (run mode)
+  aci_static_binding_to_epg:
+    <<: *primary_encap_id_present
+    description: Binding description
+  register: idempotent_description
+
+- name: Missing required param - failure message works
   aci_static_binding_to_epg:
     <<: *aci_tenant_present
   ignore_errors: yes
   register: missing_required_present
 
-- name: present assertions
+- name: Present assertions
   assert:
     that:
       - provide_present_check_mode.changed == true
@@ -97,25 +121,29 @@
       - provide_present.previous == []
       - primary_ecap_id_present.changed == true
       - 'primary_ecap_id_present.sent == {"fvRsPathAtt": {"attributes": {"primaryEncap": "vlan-50"}}}'
+      - description_cm.changed == true
+      - description.changed == true
+      - idempotent_description_cm.changed == false
+      - idempotent_description.changed == false
       - missing_required_present.failed == true
       - 'missing_required_present.msg == "missing required arguments: interface_type"'
       - missing_required_present.failed == true
 
 
-- name: get binding
+- name: Get binding
   aci_static_binding_to_epg:
     <<: *primary_encap_id_present
     state: query
   register: query_static_binding
 
-- name: missing required param - failure message works
+- name: Missing required param - failure message works
   aci_static_binding_to_epg:
     <<: *aci_tenant_present
     state: query
   ignore_errors: yes
   register: missing_required_query
 
-- name: query assertions
+- name: Query assertions
   assert:
     that:
       - query_static_binding.changed == false
@@ -128,26 +156,26 @@
       - 'missing_required_query.msg == "missing required arguments: interface_type"'
 
 
-- name: delete provide binding - deletion works
+- name: Delete provide binding - deletion works
   aci_static_binding_to_epg:
     <<: *primary_encap_id_present
     state: absent
   register: provide_absent
 
-- name: delete provide binding - idempotency works
+- name: Delete provide binding - idempotency works
   aci_static_binding_to_epg:
     <<: *primary_encap_id_present
     state: absent
   register: provide_absent_idempotent
 
-- name: missing param - failure message works
+- name: Missing param - failure message works
   aci_static_binding_to_epg:
     <<: *aci_tenant_present
     state: absent
   ignore_errors: yes
   register: missing_param_absent
 
-- name: absent assertions
+- name: Absent assertions
   assert:
     that:
       - provide_absent.changed == true
@@ -158,21 +186,21 @@
       - missing_param_absent.failed == true
       - 'missing_param_absent.msg == "missing required arguments: interface_type"'
 
-- name: cleanup binding
+- name: Cleanup binding
   aci_static_binding_to_epg:
     <<: *aci_static_binding_to_epg_absent
 
-- name: cleanup epg
+- name: Cleanup epg
   aci_epg:
     <<: *aci_epg_present
     state: absent
 
-- name: cleanup ap
+- name: Cleanup ap
   aci_ap:
     <<: *aci_ap_present
     state: absent
 
-- name: cleanup tenant
+- name: Cleanup tenant
   aci_tenant:
     <<: *aci_tenant_present
     state: absent