Repository: ansible/ansible

Issue:
nxos_snmp_host: several properties not idempotent for n1 images
<!---
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/feature -->
- nxos_snmp_host

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.4.0.0 (detached HEAD d14467b029) last updated 2017/09/27 14:54:45 (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.
-->
Ubuntu 14.04
##### SUMMARY
<!--- Explain the problem briefly -->
`show snmp host | json` output varies for this image.

##### 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: Configure snmp host
    nxos_snmp_host: &config
      snmp_host: 3.3.3.3
      community: TESTING
      version: v3
      v3: noauth
      #udp: 55
      snmp_type: inform
      vrf: management
      vrf_filter: management
      src_intf: "{{ intname|default(omit) }}"
      state: present
      provider: "{{ connection }}"
    register: result

  - assert: &true
      that:
        - "result.changed == true"

  - name: Idempotence Check
    nxos_snmp_host: *config
    register: result

  - assert: &false
      that:
        - "result.changed == false"
```

<!--- 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 play to be idempotent

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
TASK [nxos_snmp_host : Idempotence Check] *****************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_snmp_host/tests/common/sanity.yaml:46
<n5k-xxx.example.com> connection transport is cli
<n5k-xxx.example.com> using connection plugin network_cli
<n5k-xxx.example.com> socket_path: /root/.ansible/pc/1b4e3e6315
Using module file /root/agents-ci/ansible/lib/ansible/modules/network/nxos/nxos_snmp_host.py
<n5k-xxx.example.com> ESTABLISH LOCAL CONNECTION FOR USER: root
<n5k-xxx.example.com> EXEC /bin/sh -c 'echo ~ && sleep 0'
<n5k-xxx.example.com> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1506022469.74-261728230420672 `" && echo ansible-tmp-1506022469.74-261728230420672="` echo /root/.ansible/tmp/ansible-tmp-1506022469.74-261728230420672 `" ) && sleep 0'
<n5k-xxx.example.com> PUT /tmp/tmpOMKDDk TO /root/.ansible/tmp/ansible-tmp-1506022469.74-261728230420672/nxos_snmp_host.py
<n5k-xxx.example.com> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1506022469.74-261728230420672/ /root/.ansible/tmp/ansible-tmp-1506022469.74-261728230420672/nxos_snmp_host.py && sleep 0'
<n5k-xxx.example.com> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1506022469.74-261728230420672/nxos_snmp_host.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1506022469.74-261728230420672/" > /dev/null 2>&1 && sleep 0'
changed: [n5k-xxx.example.com] => {
    "changed": true, 
    "commands": [
        "snmp-server host 3.3.3.3 inform version 3 noauth TESTING",  
        "snmp-server host 3.3.3.3 filter-vrf management", 
        "snmp-server host 3.3.3.3 source-interface Ethernet1/2", 
        "snmp-server host 3.3.3.3 use-vrf management"
    ], 
    "failed": false, 
    "invocation": {
        "module_args": {
            "community": "TESTING", 
            "host": "n5k-xxx.example.com", 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "port": null, 
            "provider": {
                "host": "n5k-xxx.example.com",               
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "port": null, 
                "ssh_keyfile": null, 
                "timeout": null, 
                "transport": "cli", 
                "use_ssl": null, 
                "username": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "validate_certs": null
            }, 
            "snmp_host": "3.3.3.3", 
            "snmp_type": "inform", 
            "src_intf": "Ethernet1/2", 
            "ssh_keyfile": null, 
            "state": "present", 
            "timeout": null,  
            "transport": "cli", 
            "udp": null, 
            "use_ssl": null,  
            "username": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "v3": "noauth", 
            "validate_certs": null, 
            "version": "v3",  
            "vrf": "management", 
            "vrf_filter": "management"
        }
    }
}
 
TASK [nxos_snmp_host : assert] ****************************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_snmp_host/tests/common/sanity.yaml:50
FAILED! => {
    "assertion": "result.changed == false",    
    "changed": false, 
    "evaluated_to": false, 
    "failed": true
}
```



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 --->

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_snmp_host.py b/lib/ansible/modules/network/nxos/nxos_snmp_host.py
--- a/lib/ansible/modules/network/nxos/nxos_snmp_host.py
+++ b/lib/ansible/modules/network/nxos/nxos_snmp_host.py
@@ -150,6 +150,14 @@ def get_snmp_host(host, module):
         'secname': 'community'
     }
 
+    host_map_5k = {
+        'port': 'udp',
+        'version': 'version',
+        'sec_level': 'v3',
+        'notif_type': 'snmp_type',
+        'commun_or_user': 'community'
+    }
+
     resource = {}
 
     if body:
@@ -177,8 +185,35 @@ def get_snmp_host(host, module):
                 if vrf:
                     host_resource['vrf'] = vrf.split(':')[1].strip()
                 resource[key] = host_resource
-
-        except (KeyError, AttributeError, TypeError):
+        except KeyError:
+            # Handle the 5K case
+            try:
+                resource_table = body[0]['TABLE_hosts']['ROW_hosts']
+
+                if isinstance(resource_table, dict):
+                    resource_table = [resource_table]
+
+                for each in resource_table:
+                    key = str(each['address'])
+                    src = each.get('src_intf')
+                    host_resource = apply_key_map(host_map_5k, each)
+
+                    if src:
+                        host_resource['src_intf'] = src.split(':')[1].strip()
+
+                    vrf_filt = each.get('TABLE_filter_vrf')
+                    if vrf_filt:
+                        vrf_filter = vrf_filt['ROW_filter_vrf']['filter_vrf_name'].split(',')
+                        filters = [vrf.strip() for vrf in vrf_filter]
+                        host_resource['vrf_filter'] = filters
+
+                    vrf = each.get('use_vrf_name')
+                    if vrf:
+                        host_resource['vrf'] = vrf.strip()
+                    resource[key] = host_resource
+            except (KeyError, AttributeError, TypeError):
+                return resource
+        except (AttributeError, TypeError):
             return resource
 
         find = resource.get(host)
diff --git a/lib/ansible/modules/network/nxos/nxos_snmp_traps.py b/lib/ansible/modules/network/nxos/nxos_snmp_traps.py
--- a/lib/ansible/modules/network/nxos/nxos_snmp_traps.py
+++ b/lib/ansible/modules/network/nxos/nxos_snmp_traps.py
@@ -120,17 +120,21 @@ def get_snmp_traps(group, module):
         'isEnabled': 'enabled'
     }
 
-    resource = {}
+    trap_key_5k = {
+        'trap': 'trap',
+        'enabled': 'enabled'
+    }
 
+    resource = {}
+    feature_list = ['aaa', 'bridge', 'callhome', 'cfs', 'config',
+                    'entity', 'feature-control', 'hsrp', 'license',
+                    'link', 'lldp', 'ospf', 'pim', 'rf', 'rmon',
+                    'snmp', 'storm-control', 'stpx', 'sysmgr',
+                    'system', 'upgrade', 'vtp']
     try:
         resource_table = body[0]['TABLE_snmp_trap']['ROW_snmp_trap']
 
-        for each_feature in ['aaa', 'bridge', 'callhome', 'cfs', 'config',
-                             'entity', 'feature-control', 'hsrp', 'license',
-                             'link', 'lldp', 'ospf', 'pim', 'rf', 'rmon',
-                             'snmp', 'storm-control', 'stpx', 'sysmgr',
-                             'system', 'upgrade', 'vtp']:
-
+        for each_feature in feature_list:
             resource[each_feature] = []
 
         for each_resource in resource_table:
@@ -139,8 +143,23 @@ def get_snmp_traps(group, module):
 
             if key != 'Generic':
                 resource[key].append(mapped_trap)
-
-    except (KeyError, AttributeError):
+    except KeyError:
+        try:
+            resource_table = body[0]['TABLE_mib']['ROW_mib']
+
+            for each_feature in feature_list:
+                resource[each_feature] = []
+
+            for each_resource in resource_table:
+                key = str(each_resource['mib'])
+                each_resource = each_resource['TABLE_trap']['ROW_trap']
+                mapped_trap = apply_key_map(trap_key_5k, each_resource)
+
+                if key != 'Generic':
+                    resource[key].append(mapped_trap)
+        except (KeyError, AttributeError):
+            return resource
+    except AttributeError:
         return resource
 
     find = resource.get(group, None)
@@ -221,7 +240,6 @@ def main():
     existing = get_snmp_traps(group, module)
 
     commands = get_trap_commands(group, state, existing, module)
-
     cmds = flatten_list(commands)
     if cmds:
         results['changed'] = True


Test patch: