Repository: ansible/ansible

Issue:
nxos_igmp_snooping is not idempotent for non-default values on D1 image on N7K.
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->
nxos_igmp_snooping is not idempotent for non-default values on D1 image on N7K.

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
- nxos_igmp_snooping

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.4.0.0 (stable-2.4 06a066fe1b) last updated 2017/09/15 15:32:55 (GMT -400)
  config file = None
  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.
-->

##### SUMMARY
<!--- Explain the problem briefly -->

##### 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
    nxos_igmp_snooping: &non-default
      snooping: true
      group_timeout: never
      link_local_grp_supp: true
      report_supp: true
      v3_report_supp: true
      state: present
      provider: "{{ connection }}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
The above playbook to be idempotent across all platforms and releases

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
The playbook is not idempotent on D1 release of N7K. The reason is that some igmp snoop commands are not nvgened on running config. The real solution here is to move the command that checks the state of igmp snooping from 'sh run all | include igmp.snooping' to 'sh ip igmp snooping | json'.




Discussion:
cc @GGabriele @jedelman8 @mikewiebe @privateip @rcarrillocruz @trishnaguha
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@rahushen Thanks for the bug report. This doesn't sounds like a 2.4.0 release blocking issue, so something that can be fixed for `2.4.1`.

Would be good for the integration tests to be updated to defend this.
@rahushen Idempotence works for me on devel for NXOS 9K.
Playbook:
```
- name: nxos igmp snooping 
  hosts: nxoshost
  connection: local

  tasks:
  - name: configure igmp snooping
    nxos_igmp_snooping: &non-default
      snooping: true
      group_timeout: never
      link_local_grp_supp: true
      report_supp: true
      v3_report_supp: true
      state: present

  - name: check idempotence
    nxos_igmp_snooping: *non-default
```

Result:
```
PLAY RECAP ********************************************************************************************************************************************************************************************************
nxoshost               : ok=3    changed=1    unreachable=0    failed=0   
```
@trishnaguha the issue is seen only on D1 images running on the N7K platform. I assume that you'll see the same behavior on titanium. The module works fine for N9K.

@rahushen After testing this on NXOS7K, looks like `ip igmp snooping report-suppression` and `ip igmp snooping link-local-group-suppression` affects no config. `show run all | include igmp.snooping` does not return those commands.
Can you please confirm if it is the same for you?
@trishnaguha ... Yes, I have documented that in the 'ACTUAL RESULTS' section of the bug report. We need to use another command to gather igmp snooping info. 

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_igmp_snooping.py b/lib/ansible/modules/network/nxos/nxos_igmp_snooping.py
--- a/lib/ansible/modules/network/nxos/nxos_igmp_snooping.py
+++ b/lib/ansible/modules/network/nxos/nxos_igmp_snooping.py
@@ -110,10 +110,10 @@
 from ansible.module_utils.basic import AnsibleModule
 
 
-def execute_show_command(command, module):
+def execute_show_command(command, module, output='text'):
     command = {
         'command': command,
-        'output': 'text',
+        'output': output,
     }
 
     return run_commands(module, [command])
@@ -130,11 +130,11 @@ def flatten_list(command_lists):
 
 
 def get_group_timeout(config):
-    command = 'ip igmp snooping group-timeout'
-    REGEX = re.compile(r'(?:{0}\s)(?P<value>.*)$'.format(command), re.M)
-    value = ''
-    if command in config:
-        value = REGEX.search(config).group('value')
+    match = re.search(r'  Group timeout configured: (\S+)', config, re.M)
+    if match:
+        value = match.group(1)
+    else:
+        value = ''
     return value
 
 
@@ -150,33 +150,42 @@ def get_snooping(config):
 
 
 def get_igmp_snooping(module):
-    command = 'show run all | include igmp.snooping'
+    command = 'show ip igmp snooping'
     existing = {}
-    body = execute_show_command(command, module)[0]
 
-    if body:
-        split_body = body.splitlines()
+    try:
+        body = execute_show_command(command, module, output='json')[0]
+    except IndexError:
+        body = []
 
-        if 'no ip igmp snooping' in split_body:
-            existing['snooping'] = False
-        else:
+    if body:
+        snooping = str(body.get('enabled')).lower()
+        if snooping == 'true' or snooping == 'enabled':
             existing['snooping'] = True
+        else:
+            existing['snooping'] = False
 
-        if 'no ip igmp snooping report-suppression' in split_body:
-            existing['report_supp'] = False
-        elif 'ip igmp snooping report-suppression' in split_body:
+        report_supp = str(body.get('grepsup')).lower()
+        if report_supp == 'true' or report_supp == 'enabled':
             existing['report_supp'] = True
+        else:
+            existing['report_supp'] = False
 
-        if 'no ip igmp snooping link-local-groups-suppression' in split_body:
-            existing['link_local_grp_supp'] = False
-        elif 'ip igmp snooping link-local-groups-suppression' in split_body:
+        link_local_grp_supp = str(body.get('glinklocalgrpsup')).lower()
+        if link_local_grp_supp == 'true' or link_local_grp_supp == 'enabled':
             existing['link_local_grp_supp'] = True
+        else:
+            existing['link_local_grp_supp'] = False
 
-        if 'ip igmp snooping v3-report-suppression' in split_body:
+        v3_report_supp = str(body.get('gv3repsup')).lower()
+        if v3_report_supp == 'true' or v3_report_supp == 'enabled':
             existing['v3_report_supp'] = True
         else:
             existing['v3_report_supp'] = False
 
+    command = 'show ip igmp snooping'
+    body = execute_show_command(command, module)[0]
+    if body:
         existing['group_timeout'] = get_group_timeout(body)
 
     return existing
@@ -260,7 +269,6 @@ def main():
                     if value is not None)
 
     existing = get_igmp_snooping(module)
-    end_state = existing
 
     commands = []
     if state == 'present':


Test patch: