Repository: ansible/ansible

Issue:
(P2) nxos_pim_interface is not idempotent
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
 - nxos_pim_interface

##### NXOS VERSION
7.3(0)D1(1), 8.0(1), 7.0(3)I7(1), 7.0(3)I4(6)

##### ANSIBLE VERSION
```
ansible 2.4.0 (devel 0fb64214a4) last updated 2017/07/27 13:55:58 (GMT -400)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/usr/share/ansible']
  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
Ubuntu 14.04

##### SUMMARY
The following playbook demonstrates the idempotency problem for the nxos versions listed above.

```yaml
---
- debug: msg="START TRANSPORT:{{ connection.transport }} nxos_pim_interface sanity test"

- name: "Disable feature PIM"
  nxos_feature: &disable_feature
    feature: pim
    state: disabled
    provider: "{{ connection }}"

- name: "Enable feature PIM"
  nxos_feature:
    feature: pim
    state: enabled
    provider: "{{ connection }}"

- set_fact: testint="{{ nxos_int1 }}"

- name: "Ensure {{testint}} is layer3"
  nxos_interface:
    interface: "{{ testint }}"
    mode: layer3
    description: 'Configured by Ansible - Layer3'
    admin_state: 'up'
    state: present
    provider: "{{ connection }}"

- name: configure
  nxos_pim_interface:
    interface: "{{ testint }}"
    state: absent
    provider: "{{ connection }}"

- block:
  - name: configure pim interface
    nxos_pim_interface: &config
      interface: "{{ testint }}"
      dr_prio: 10
      hello_interval: 40
      state: present
      provider: "{{ connection }}"
    register: result

  - assert: &true
      that:
        - "result.changed == true"

  - name: Check idempotence
    nxos_pim_interface: *config
    register: result

  - assert: &false
      that:
        - "result.changed == false"

  - name: configure gp policy and type
    nxos_pim_interface: &configjp
      interface: "{{ testint }}" 
      jp_policy_in: JPIN
      jp_policy_out: JPOUT
      jp_type_in: routemap
      jp_type_out: routemap
      provider: "{{ connection }}"
    register: result

  - assert: *true

  - name: Check idempotence
    nxos_pim_interface: *configjp
    register: result

  - assert: *false

  - name: configure state default
    nxos_pim_interface: &configdefault
      interface: "{{ testint }}" 
      state: default
      provider: "{{ connection }}"
    register: result

  - assert: *true

  - name: Check idempotence
    nxos_pim_interface: *configdefault
    register: result

  - assert: *false

  always:
  - name: "Disable feature PIM"
    nxos_feature: *disable_feature

- debug: msg="END TRANSPORT:{{ connection.transport }} nxos_pim_interface sanity test"
```


Discussion:
cc @GGabriele @jedelman8 @privateip @rahushen @rcarrillocruz @trishnaguha
[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/nxos/nxos_pim_interface.py b/lib/ansible/modules/network/nxos/nxos_pim_interface.py
--- a/lib/ansible/modules/network/nxos/nxos_pim_interface.py
+++ b/lib/ansible/modules/network/nxos/nxos_pim_interface.py
@@ -203,7 +203,6 @@ def local_existing(gexisting):
         if jp_bidir and isauth:
             gexisting.pop('jp_bidir')
             gexisting.pop('isauth')
-        gexisting['sparse'] = True
 
     return gexisting, jp_bidir, isauth
 
@@ -226,9 +225,9 @@ def get_interface_type(interface):
 
 
 def get_interface_mode(interface, intf_type, module):
-    command = 'show interface {0} | json'.format(interface)
     mode = 'unknown'
-    body = run_commands(module, [command])
+    command = 'show interface {0}'.format(interface)
+    body = execute_show_command(command, module)
 
     try:
         interface_table = body[0]['TABLE_interface']['ROW_interface']
@@ -249,7 +248,6 @@ def get_interface_mode(interface, intf_type, module):
 def get_pim_interface(module, interface):
     pim_interface = {}
     command = 'show ip pim interface {0}'.format(interface)
-
     body = execute_show_command(command, module, text=True)
 
     if body:
@@ -257,19 +255,19 @@ def get_pim_interface(module, interface):
             body = execute_show_command(command, module)
 
     try:
-        get_data = body[0]['TABLE_iod']['ROW_iod']
+        get_data = body[0]['TABLE_vrf']['ROW_vrf']['TABLE_iod']['ROW_iod']
 
-        if isinstance(get_data.get('dr-priority'), string_types):
-            pim_interface['dr_prio'] = get_data.get('dr-priority')
-        else:
+        if isinstance(get_data.get('dr-priority'), list):
             pim_interface['dr_prio'] = get_data.get('dr-priority')[0]
+        else:
+            pim_interface['dr_prio'] = str(get_data.get('dr-priority'))
 
         hello_interval = get_data.get('hello-interval-sec')
         if hello_interval:
             hello_interval_msec = int(get_data.get('hello-interval-sec')) * 1000
         pim_interface['hello_interval'] = str(hello_interval_msec)
-        border = get_data.get('is-border')
 
+        border = get_data.get('is-border')
         if border == 'true':
             pim_interface['border'] = True
         elif border == 'false':
@@ -293,8 +291,7 @@ def get_pim_interface(module, interface):
         if isinstance(get_data.get('jp-out-policy-name'), string_types):
             pim_interface['jp_policy_out'] = get_data.get('jp-out-policy-name')
         else:
-            pim_interface['jp_policy_out'] = get_data.get(
-                'jp-out-policy-name')[0]
+            pim_interface['jp_policy_out'] = get_data.get('jp-out-policy-name')[0]
 
         if pim_interface['jp_policy_out'] == 'none configured':
             pim_interface['jp_policy_out'] = None
@@ -307,13 +304,15 @@ def get_pim_interface(module, interface):
     jp_configs = []
     neigh = None
     if body:
-        all_lines = body[0].splitlines()
+        all_lines = body.splitlines()
 
         for each in all_lines:
             if 'jp-policy' in each:
                 jp_configs.append(str(each.strip()))
             elif 'neighbor-policy' in each:
                 neigh = str(each)
+            elif 'sparse-mode' in each:
+                pim_interface['sparse'] = True
 
     pim_interface['neighbor_type'] = None
     neigh_type = None


Test patch:
diff --git a/test/units/modules/network/nxos/fixtures/nxos_pim_interface/config.cfg b/test/units/modules/network/nxos/fixtures/nxos_pim_interface/config.cfg
new file mode 100644
--- /dev/null
+++ b/test/units/modules/network/nxos/fixtures/nxos_pim_interface/config.cfg
@@ -0,0 +1,11 @@
+!Command: show running-config interface Ethernet2/1
+!Time: Mon Aug 21 17:22:02 2017
+
+version 7.3(0)D1(1)
+
+interface Ethernet2/1
+  description Configured by Ansible - Layer3
+  no switchport
+  mac-address fa16.3e00.0006
+  ip address 10.0.0.69/30
+  no shutdown
diff --git a/test/units/modules/network/nxos/fixtures/nxos_pim_interface/show_ip_pim_interface_eth2_1 b/test/units/modules/network/nxos/fixtures/nxos_pim_interface/show_ip_pim_interface_eth2_1
--- a/test/units/modules/network/nxos/fixtures/nxos_pim_interface/show_ip_pim_interface_eth2_1
+++ b/test/units/modules/network/nxos/fixtures/nxos_pim_interface/show_ip_pim_interface_eth2_1
@@ -8,19 +8,17 @@
           "if-status": "protocol-up/link-up/admin-up",
           "if-addr-summary": "IP address: 10.0.0.45, IP subnet: 10.0.0.44/30",
           "pim-dr-address": "10.0.0.45",
-          "dr-priority": 1,
+          "dr-priority": 2,
           "nbr-cnt": 0,
-          "hello-interval-sec": 30,
+          "hello-interval-sec": 35,
           "hello-timer": "PT3S",
           "holdtime-sec": 105,
           "if-conf-dr-priority": 1,
           "if-conf-delay": 3,
-          "is-border": "false",
+          "is-border": "true",
           "genid": "38c4b959",
           "isauth-config": "false",
           "nbr-policy-name": "none configured",
-          "jp-in-policy-name": "JPIN",
-          "jp-out-policy-name": "JPOUT",
           "jp-interval": 1,
           "jp-next-send": 1,
           "pim-bfd-enabled": "no",
diff --git a/test/units/modules/network/nxos/test_nxos_pim_interface.py b/test/units/modules/network/nxos/test_nxos_pim_interface.py
--- a/test/units/modules/network/nxos/test_nxos_pim_interface.py
+++ b/test/units/modules/network/nxos/test_nxos_pim_interface.py
@@ -29,6 +29,9 @@ class TestNxosIPInterfaceModule(TestNxosModule):
     module = nxos_pim_interface
 
     def setUp(self):
+        self.mock_get_config = patch('ansible.modules.network.nxos.nxos_pim_interface.get_config')
+        self.get_config = self.mock_get_config.start()
+
         self.mock_load_config = patch('ansible.modules.network.nxos.nxos_pim_interface.load_config')
         self.load_config = self.mock_load_config.start()
 
@@ -36,6 +39,7 @@ def setUp(self):
         self.run_commands = self.mock_run_commands.start()
 
     def tearDown(self):
+        self.mock_get_config.stop()
         self.mock_load_config.stop()
         self.mock_run_commands.stop()
 
@@ -53,15 +57,17 @@ def load_from_file(*args, **kwargs):
                 output.append(load_fixture(module_name, filename))
             return output
 
+        self.get_config.return_value = load_fixture(module_name, 'config.cfg')
         self.load_config.return_value = None
         self.run_commands.side_effect = load_from_file
 
     def test_nxos_pim_interface_present(self):
-        set_module_args(dict(interface='eth2/1', dr_prio=10, hello_interval=40))
+        set_module_args(dict(interface='eth2/1', dr_prio=10, hello_interval=40, sparse=True, border=True))
         self.execute_module(
             changed=True,
-            commands=['interface eth2/1', 'ip pim dr-priority 10',
-                      'ip pim hello-interval 40000', 'ip pim sparse-mode']
+            commands=[
+                'interface eth2/1', 'ip pim dr-priority 10', 'ip pim hello-interval 40000',
+                'ip pim sparse-mode', 'ip pim border']
         )
 
     def test_nxos_pim_interface_jp(self):
@@ -79,8 +85,7 @@ def test_nxos_pim_interface_default(self):
         set_module_args(dict(interface='eth2/1', state='default'))
         self.execute_module(
             changed=True,
-            commands=['interface eth2/1', 'ip pim dr-priority 1',
-                      'no ip pim border', 'ip pim hello-interval 30000']
+            commands=['interface eth2/1', 'ip pim dr-priority 1', 'ip pim hello-interval 30000', 'no ip pim border']
         )
 
     def test_nxos_pim_interface_ip_absent(self):