Repository: ansible/ansible

Issue:
nxos_evpn_global: Handling platform differences and feature unavailability.
<!---
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_evpn_global

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.4.0 (devel 6ba49f42c2) last updated 2017/07/14 16:00:11 (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.
-->
- Ansible Server : Ubuntu 14.04
- Device: Nexus9000 C9508  running 7.0(3)I6(1)

##### SUMMARY
<!--- Explain the problem briefly -->
The 'nv overlay evpn' command is not available on Nexus 3000 series of switches. The module currently doesn't handle this scenario. It fails with 'Invalid command' , but I think this needs to be handled more elegantly. Also, documentation needs to be improved to highlight this. This issue isn't necessarily a evpn issue, but can be generalized to several modules and how they'll handle platform differences.

##### 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.
-->
Run the playbook againt a Nexus 3000 device
<!--- Paste example playbooks or commands between quotes below -->
```yaml
  - name: "Enable nv overlay evpn"
    nxos_evpn_global: &enable_evpn
      nv_overlay_evpn: true
      provider: "{{ cli }}"
```

<!--- You can also paste gist.github.com links for larger files -->



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 --->
cc @kedarX
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Target Date 02/01/2018

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_evpn_global.py b/lib/ansible/modules/network/nxos/nxos_evpn_global.py
--- a/lib/ansible/modules/network/nxos/nxos_evpn_global.py
+++ b/lib/ansible/modules/network/nxos/nxos_evpn_global.py
@@ -29,6 +29,8 @@
 description:
     - Handles the EVPN control plane for VXLAN.
 author: Gabriele Gerbino (@GGabriele)
+notes:
+  - This module is not supported on Nexus 3000 series of switches.
 options:
   nv_overlay_evpn:
     description:
@@ -52,13 +54,10 @@
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.network.nxos.nxos import get_config, load_config
-from ansible.module_utils.network.nxos.nxos import nxos_argument_spec
-from ansible.module_utils.network.nxos.nxos import check_args as nxos_check_args
+from ansible.module_utils.network.nxos.nxos import get_capabilities, nxos_argument_spec
 
 
 def check_args(module, warnings):
-    nxos_check_args(module, warnings)
-
     for key in ('include_defaults', 'config', 'save'):
         if module.params[key] is not None:
             warnings.append('argument %s is no longer supported, ignoring value' % key)
@@ -88,6 +87,12 @@ def main():
     config = get_config(module)
     commands = list()
 
+    info = get_capabilities(module).get('device_info', {})
+    os_platform = info.get('network_os_platform', '')
+
+    if '3K' in os_platform:
+        module.fail_json(msg='This module is not supported on Nexus 3000 series')
+
     if module.params['nv_overlay_evpn'] is True:
         if 'nv overlay evpn' not in config:
             commands.append('nv overlay evpn')
diff --git a/lib/ansible/modules/network/nxos/nxos_feature.py b/lib/ansible/modules/network/nxos/nxos_feature.py
--- a/lib/ansible/modules/network/nxos/nxos_feature.py
+++ b/lib/ansible/modules/network/nxos/nxos_feature.py
@@ -73,13 +73,10 @@
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.network.nxos.nxos import load_config, run_commands
-from ansible.module_utils.network.nxos.nxos import nxos_argument_spec
-from ansible.module_utils.network.nxos.nxos import check_args as nxos_check_args
+from ansible.module_utils.network.nxos.nxos import get_capabilities, nxos_argument_spec
 
 
 def check_args(module, warnings):
-    nxos_check_args(module, warnings)
-
     for key in ('include_defaults', 'config', 'save'):
         if module.params[key] is not None:
             warnings.append('argument %s is no longer supported, ignoring value' % key)
@@ -139,39 +136,73 @@ def validate_feature(module, mode='show'):
     how they are configured'''
 
     feature = module.params['feature']
-
-    feature_to_be_mapped = {
-        'show': {
-            'nv overlay': 'nve',
-            'vn-segment-vlan-based': 'vnseg_vlan',
-            'hsrp': 'hsrp_engine',
-            'fabric multicast': 'fabric_mcast',
-            'scp-server': 'scpServer',
-            'sftp-server': 'sftpServer',
-            'sla responder': 'sla_responder',
-            'sla sender': 'sla_sender',
-            'ssh': 'sshServer',
-            'tacacs+': 'tacacs',
-            'telnet': 'telnetServer',
-            'ethernet-link-oam': 'elo',
-            'port-security': 'eth_port_sec'
-        },
-        'config': {
-            'nve': 'nv overlay',
-            'vnseg_vlan': 'vn-segment-vlan-based',
-            'hsrp_engine': 'hsrp',
-            'fabric_mcast': 'fabric multicast',
-            'scpServer': 'scp-server',
-            'sftpServer': 'sftp-server',
-            'sla_sender': 'sla sender',
-            'sla_responder': 'sla responder',
-            'sshServer': 'ssh',
-            'tacacs': 'tacacs+',
-            'telnetServer': 'telnet',
-            'elo': 'ethernet-link-oam',
-            'eth_port_sec': 'port-security'
+    info = get_capabilities(module).get('device_info', {})
+    os_version = info.get('network_os_version', '')
+
+    if '8.1' in os_version:
+        feature_to_be_mapped = {
+            'show': {
+                'nv overlay': 'nve',
+                'vn-segment-vlan-based': 'vnseg_vlan',
+                'hsrp': 'hsrp_engine',
+                'fabric multicast': 'fabric_mcast',
+                'scp-server': 'scpServer',
+                'sftp-server': 'sftpServer',
+                'sla responder': 'sla_responder',
+                'sla sender': 'sla_sender',
+                'ssh': 'sshServer',
+                'tacacs+': 'tacacs',
+                'telnet': 'telnetServer',
+                'ethernet-link-oam': 'elo'
+            },
+            'config': {
+                'nve': 'nv overlay',
+                'vnseg_vlan': 'vn-segment-vlan-based',
+                'hsrp_engine': 'hsrp',
+                'fabric_mcast': 'fabric multicast',
+                'scpServer': 'scp-server',
+                'sftpServer': 'sftp-server',
+                'sla_sender': 'sla sender',
+                'sla_responder': 'sla responder',
+                'sshServer': 'ssh',
+                'tacacs': 'tacacs+',
+                'telnetServer': 'telnet',
+                'elo': 'ethernet-link-oam'
+            }
+        }
+    else:
+        feature_to_be_mapped = {
+            'show': {
+                'nv overlay': 'nve',
+                'vn-segment-vlan-based': 'vnseg_vlan',
+                'hsrp': 'hsrp_engine',
+                'fabric multicast': 'fabric_mcast',
+                'scp-server': 'scpServer',
+                'sftp-server': 'sftpServer',
+                'sla responder': 'sla_responder',
+                'sla sender': 'sla_sender',
+                'ssh': 'sshServer',
+                'tacacs+': 'tacacs',
+                'telnet': 'telnetServer',
+                'ethernet-link-oam': 'elo',
+                'port-security': 'eth_port_sec'
+            },
+            'config': {
+                'nve': 'nv overlay',
+                'vnseg_vlan': 'vn-segment-vlan-based',
+                'hsrp_engine': 'hsrp',
+                'fabric_mcast': 'fabric multicast',
+                'scpServer': 'scp-server',
+                'sftpServer': 'sftp-server',
+                'sla_sender': 'sla sender',
+                'sla_responder': 'sla responder',
+                'sshServer': 'ssh',
+                'tacacs': 'tacacs+',
+                'telnetServer': 'telnet',
+                'elo': 'ethernet-link-oam',
+                'eth_port_sec': 'port-security'
+            }
         }
-    }
 
     if feature in feature_to_be_mapped[mode]:
         feature = feature_to_be_mapped[mode][feature]


Test patch:
diff --git a/test/units/modules/network/nxos/test_nxos_evpn_global.py b/test/units/modules/network/nxos/test_nxos_evpn_global.py
--- a/test/units/modules/network/nxos/test_nxos_evpn_global.py
+++ b/test/units/modules/network/nxos/test_nxos_evpn_global.py
@@ -37,10 +37,15 @@ def setUp(self):
         self.mock_load_config = patch('ansible.modules.network.nxos.nxos_evpn_global.load_config')
         self.load_config = self.mock_load_config.start()
 
+        self.mock_get_capabilities = patch('ansible.modules.network.nxos.nxos_evpn_global.get_capabilities')
+        self.get_capabilities = self.mock_get_capabilities.start()
+        self.get_capabilities.return_value = {'network_api': 'cliconf'}
+
     def tearDown(self):
         super(TestNxosEvpnGlobalModule, self).tearDown()
         self.mock_get_config.stop()
         self.mock_load_config.stop()
+        self.mock_get_capabilities.stop()
 
     def load_fixtures(self, commands=None, device=''):
         self.load_config.return_value = None
diff --git a/test/units/modules/network/nxos/test_nxos_feature.py b/test/units/modules/network/nxos/test_nxos_feature.py
--- a/test/units/modules/network/nxos/test_nxos_feature.py
+++ b/test/units/modules/network/nxos/test_nxos_feature.py
@@ -38,10 +38,15 @@ def setUp(self):
         self.mock_load_config = patch('ansible.modules.network.nxos.nxos_feature.load_config')
         self.load_config = self.mock_load_config.start()
 
+        self.mock_get_capabilities = patch('ansible.modules.network.nxos.nxos_feature.get_capabilities')
+        self.get_capabilities = self.mock_get_capabilities.start()
+        self.get_capabilities.return_value = {'network_api': 'cliconf'}
+
     def tearDown(self):
         super(TestNxosFeatureModule, self).tearDown()
         self.mock_run_commands.stop()
         self.mock_load_config.stop()
+        self.mock_get_capabilities.stop()
 
     def load_fixtures(self, commands=None, device=''):
         def load_from_file(*args, **kwargs):