Repository: ansible/ansible

Issue:
nxos_pim_rp_address issues
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
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
-->
nxos_pim_rp_address
##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.0 (devel 788010d0f0) last updated 2018/01/08 11:49:21 (GMT -400)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /root/agents-ci/ansible/lib/ansible
  executable location = /root/agents-ci/ansible/bin/ansible
  python version = 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4]
```

##### OS / ENVIRONMENT
<!---
Mention 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.
-->
Ansible Server : Ubuntu 14.04
Device: N9K running 7.0(3)I5(2)
##### SUMMARY
<!--- Explain the problem briefly -->

There are multiple issues with nxos_pim_rp_address
1. Currently we can add a new pim rp_address with group_list. However, the platform allows adding pim rp_address without group_list, IN ADDITION TO, the one with group_list. This is not possible with the current code.
2. Deletion of a pim rp_address resource has multiple forms in different platforms and the ones using long form will not work on certain platforms which need short form. Currently we can only use nxos_config to get around this.
3. Some platforms show default group_list in the show command and this is causing issues while getting the existing configuration.
4. The integration test cases are very limited currently.

Note: I will open a PR shortly to address these above issues


Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_pim_rp_address.py b/lib/ansible/modules/network/nxos/nxos_pim_rp_address.py
--- a/lib/ansible/modules/network/nxos/nxos_pim_rp_address.py
+++ b/lib/ansible/modules/network/nxos/nxos_pim_rp_address.py
@@ -94,7 +94,7 @@
 from ansible.module_utils.network.common.config import CustomNetworkConfig
 
 
-def get_existing(module, args):
+def get_existing(module, args, gl):
     existing = {}
     config = str(get_config(module))
     address = module.params['rp_address']
@@ -105,6 +105,11 @@ def get_existing(module, args):
         values = line.split()
         if values[0] != address:
             continue
+        if gl and 'group-list' not in line:
+            continue
+        elif not gl and 'group-list' in line:
+            if '224.0.0.0/4' not in line:  # ignore default group-list
+                continue
 
         existing['bidir'] = existing.get('bidir') or 'bidir' in line
         if len(values) > 2:
@@ -114,7 +119,8 @@ def get_existing(module, args):
             elif values[1] == 'prefix-list':
                 existing['prefix_list'] = value
             elif values[1] == 'group-list':
-                existing['group_list'] = value
+                if value != '224.0.0.0/4':  # ignore default group-list
+                    existing['group_list'] = value
 
     return existing
 
@@ -122,6 +128,14 @@ def get_existing(module, args):
 def state_present(module, existing, proposed, candidate):
     address = module.params['rp_address']
     command = 'ip pim rp-address {0}'.format(address)
+    if module.params['group_list'] and not proposed.get('group_list'):
+        command += ' group-list ' + module.params['group_list']
+    if module.params['prefix_list']:
+        if not proposed.get('prefix_list'):
+            command += ' prefix-list ' + module.params['prefix_list']
+    if module.params['route_map']:
+        if not proposed.get('route_map'):
+            command += ' route-map ' + module.params['route_map']
     commands = build_command(proposed, command)
     if commands:
         candidate.add(commands, parents=[])
@@ -140,13 +154,31 @@ def build_command(param_dict, command):
 def state_absent(module, existing, candidate):
     address = module.params['rp_address']
 
+    commands = []
     command = 'no ip pim rp-address {0}'.format(address)
-    if existing.get('group_list'):
+    if module.params['group_list'] == existing.get('group_list'):
         commands = build_command(existing, command)
-    else:
+    elif not module.params['group_list']:
         commands = [command]
 
-    candidate.add(commands, parents=[])
+    if commands:
+        candidate.add(commands, parents=[])
+
+
+def get_proposed(pargs, existing):
+    proposed = {}
+
+    for key, value in pargs.items():
+        if key != 'rp_address':
+            if str(value).lower() == 'true':
+                value = True
+            elif str(value).lower() == 'false':
+                value = False
+
+            if existing.get(key) != value:
+                proposed[key] = value
+
+    return proposed
 
 
 def main():
@@ -180,20 +212,16 @@ def main():
         'bidir'
     ]
 
-    existing = get_existing(module, args)
     proposed_args = dict((k, v) for k, v in module.params.items()
                          if v is not None and k in args)
 
-    proposed = {}
-    for key, value in proposed_args.items():
-        if key != 'rp_address':
-            if str(value).lower() == 'true':
-                value = True
-            elif str(value).lower() == 'false':
-                value = False
+    if module.params['group_list']:
+        existing = get_existing(module, args, True)
+        proposed = get_proposed(proposed_args, existing)
 
-            if existing.get(key) != value:
-                proposed[key] = value
+    else:
+        existing = get_existing(module, args, False)
+        proposed = get_proposed(proposed_args, existing)
 
     candidate = CustomNetworkConfig(indent=3)
     if state == 'present' and (proposed or not existing):
@@ -205,7 +233,19 @@ def main():
         candidate = candidate.items_text()
         result['commands'] = candidate
         result['changed'] = True
-        load_config(module, candidate)
+        msgs = load_config(module, candidate, True)
+        if msgs:
+            for item in msgs:
+                if item:
+                    if isinstance(item, dict):
+                        err_str = item['clierror']
+                    else:
+                        err_str = item
+                    if 'No policy was configured' in err_str:
+                        if state == 'absent':
+                            addr = module.params['rp_address']
+                            new_cmd = 'no ip pim rp-address {0}'.format(addr)
+                            load_config(module, new_cmd)
 
     module.exit_json(**result)
 


Test patch:
diff --git a/test/integration/targets/nxos_pim_rp_address/tests/common/configure.yaml b/test/integration/targets/nxos_pim_rp_address/tests/common/configure.yaml
--- a/test/integration/targets/nxos_pim_rp_address/tests/common/configure.yaml
+++ b/test/integration/targets/nxos_pim_rp_address/tests/common/configure.yaml
@@ -3,13 +3,6 @@
 - debug: msg="Using provider={{ connection.transport }}"
   when: ansible_connection == "local"
 
-- set_fact: bidir="false" 
-  when: platform is search('N3K')
-
-- set_fact: bidircfg='bidir'
-- set_fact: bidircfg=''
-  when: platform is search('N3K')
-
 - block:
   - name: "Disable feature PIM"
     nxos_feature: &disable_feature
@@ -27,7 +20,7 @@
     nxos_pim_rp_address: &configgl
       rp_address: "10.1.1.20"
       group_list: "224.0.0.0/8"
-      bidir: "{{bidir|default('true')}}"
+      bidir: True
       state: present
       provider: "{{ connection }}"
     register: result
@@ -44,86 +37,187 @@
       that:
         - "result.changed == false"
 
-  - name: Remove rp_address + group_list using long config
-    #FIXME: Config deletion shall be fixed in 2.5 for platform dependencies.
-    
-    # Note: For 2.4 - Use platform specific config delete command as shown below.
-    # Possible options: Identify and use the command that is supported 
-    # by your platform under test.
+  - name: Configure rp_address + group_list remove bidir
+    nxos_pim_rp_address: &configglnb
+      rp_address: "10.1.1.20"
+      group_list: "224.0.0.0/8"
+      bidir: False
+      state: present
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
+
+  - name: Check idempotence rp_address + group_list remove bidir
+    nxos_pim_rp_address: *configglnb
+    register: result
 
-    # no ip pim rp-address <ip address> <group-list|prefix-list|route-map> bidir
-    # no ip pim rp-address <ip address>
-    # no ip pim rp-address <ip address> <group-list|prefix-list|route-map>
+  - assert: *false
 
-    nxos_config:
-      lines: "no ip pim rp-address 10.1.1.20 group-list 224.0.0.0/8 {{ bidircfg }}" 
+  - name: Configure rp_address + bidir 
+    nxos_pim_rp_address: &configbi
+      rp_address: "10.1.1.20"
+      bidir: True
+      state: present
       provider: "{{ connection }}"
-    ignore_errors: yes
+    register: result
+
+  - assert: *true
+
+  - name: Check idempotence rp_address + bidir
+    nxos_pim_rp_address: *configbi
+    register: result
+
+  - assert: *false
 
-  - name: Remove rp_address + group_list using short config
-    nxos_config:
-      lines: no ip pim rp-address 10.1.1.20
+  - name: Configure rp_address remove bidir 
+    nxos_pim_rp_address: &confignbi
+      rp_address: "10.1.1.20"
+      bidir: False
+      state: present
       provider: "{{ connection }}"
-    ignore_errors: yes
+    register: result
+
+  - assert: *true
+
+  - name: Check idempotence rp_address remove bidir
+    nxos_pim_rp_address: *confignbi
+    register: result
+
+  - assert: *false
+
+  - name: Remove rp_address + group_list 
+    nxos_pim_rp_address: &configglr
+      rp_address: "10.1.1.20"
+      group_list: "224.0.0.0/8"
+      state: absent
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
+
+  - name: Check remove idempotence rp_address + group_list
+    nxos_pim_rp_address: *configglr
+    register: result
+
+  - assert: *false
+
+  - name: Remove rp_address
+    nxos_pim_rp_address: &configbir
+      rp_address: "10.1.1.20"
+      state: absent
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
 
-  - name: Configure rp_address + prefix_list 
+  - name: Check remove idempotence rp_address
+    nxos_pim_rp_address: *configbir
+    register: result
+
+  - assert: *false
+
+  - name: Configure rp_address + prefix_list + bidir
     nxos_pim_rp_address: &configpl
       rp_address: "10.1.1.20"
       prefix_list: "pim_prefix_list"
-      bidir: "{{bidir|default('true')}}"
+      bidir: True
       state: present
       provider: "{{ connection }}"
     register: result
 
   - assert: *true
 
-  - name: Check idempotence rp_address + prefix_list
+  - name: Check idempotence rp_address + prefix_list + bidir
     nxos_pim_rp_address: *configpl
     register: result
 
   - assert: *false
 
-  - name: Remove rp_address + prefix_list using long config
-    nxos_config:
-      lines: "no ip pim rp-address 10.1.1.20 prefix-list pim_prefix_list {{ bidircfg }}"
+  - name: Configure rp_address + prefix_list
+    nxos_pim_rp_address: &configplnbi
+      rp_address: "10.1.1.20"
+      prefix_list: "pim_prefix_list"
+      bidir: False
+      state: present
       provider: "{{ connection }}"
-    ignore_errors: yes
+    register: result
+
+  - assert: *true
+
+  - name: Check idempotence rp_address + prefix_list
+    nxos_pim_rp_address: *configplnbi
+    register: result
+
+  - assert: *false
 
-  - name: Remove rp_address + prefix_list using short config
-    nxos_config:
-      lines: no ip pim rp-address 10.1.1.20
+  - name: Remove rp_address + prefix_list 
+    nxos_pim_rp_address: &configplr
+      rp_address: "10.1.1.20"
+      prefix_list: "pim_prefix_list"
+      bidir: False
+      state: absent
       provider: "{{ connection }}"
-    ignore_errors: yes
+    register: result
 
-  - name: Configure rp_address + route_map 
+  - assert: *true
+
+  - name: Check remove idempotence rp_address + prefix_list
+    nxos_pim_rp_address: *configplr
+    register: result
+
+  - assert: *false
+
+  - name: Configure rp_address + route_map + bidir
     nxos_pim_rp_address: &configrm
       rp_address: "10.1.1.20"
       route_map: "pim_routemap"
-      bidir: "{{bidir|default('true')}}"
+      bidir: True
       state: present
       provider: "{{ connection }}"
     register: result
 
   - assert: *true
 
-  - name: Check idempotence rp_address + route_map
+  - name: Check idempotence rp_address + route_map + bidir
     nxos_pim_rp_address: *configrm
     register: result
 
   - assert: *false
 
-  - name: Remove rp_address + route_map using long config
-    nxos_config:
-      lines: "no ip pim rp-address 10.1.1.20 route-map pim_routemap {{ bidircfg }}"
+  - name: Configure rp_address + route_map 
+    nxos_pim_rp_address: &configrmnbi
+      rp_address: "10.1.1.20"
+      route_map: "pim_routemap"
+      bidir: False
+      state: present
       provider: "{{ connection }}"
-    ignore_errors: yes
+    register: result
+
+  - assert: *true
 
-  - name: Remove rp_address + route_map using short config
-    nxos_config:
-      lines: no ip pim rp-address 10.1.1.20
+  - name: Check idempotence rp_address + route_map
+    nxos_pim_rp_address: *configrmnbi
+    register: result
+
+  - assert: *false
+
+  - name: Remove rp_address + route_map 
+    nxos_pim_rp_address: &configrmr
+      rp_address: "10.1.1.20"
+      route_map: "pim_routemap"
+      bidir: False
+      state: absent
       provider: "{{ connection }}"
-    ignore_errors: yes
+    register: result
 
+  - assert: *true
+
+  - name: Check remove idempotence rp_address + route_map
+    nxos_pim_rp_address: *configrmr
+    register: result
+
+  - assert: *false
 
   always:
     - name: "Disable feature PIM"