Repository: ansible/ansible

Issue:
nxos_evpn_vni issues
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel 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_evpn_vni
##### 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_vpc module

1. Setting parameters to default is not working
2. Some of the parameters are not idempotent
3. There are documentation errors
4. There are unnecessary sleep in the code
5. The code is generating warnings and no commands for arbitrary reasons.
6. If we set route_targets without setting route_distinguisher in the same task, the code does nothing.

Note: I will open a PR shortly to address these above issues


Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_evpn_vni.py b/lib/ansible/modules/network/nxos/nxos_evpn_vni.py
--- a/lib/ansible/modules/network/nxos/nxos_evpn_vni.py
+++ b/lib/ansible/modules/network/nxos/nxos_evpn_vni.py
@@ -71,7 +71,7 @@
     default: null
   route_target_export:
     description:
-      - Sets the route-target 'import' extended communities.
+      - Sets the route-target 'export' extended communities.
     required: false
     default: null
   state:
@@ -104,7 +104,6 @@
 '''
 
 import re
-import time
 from ansible.module_utils.network.nxos.nxos import get_config, load_config, run_commands
 from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args
 from ansible.module_utils.basic import AnsibleModule
@@ -158,11 +157,11 @@ def get_existing(module, args):
                     existing[arg] = get_route_target_value(arg, config, module)
 
         existing_fix = dict((k, v) for k, v in existing.items() if v)
-        if existing_fix:
-            existing['vni'] = module.params['vni']
-        else:
+        if not existing_fix:
             existing = existing_fix
 
+        existing['vni'] = module.params['vni']
+
     return existing
 
 
@@ -205,12 +204,21 @@ def state_present(module, existing, proposed):
                         commands.append('no {0} {1}'.format(key, target))
             elif not isinstance(value, list):
                 value = [value]
+
             for target in value:
+                if target == 'default':
+                    continue
                 if existing:
                     if target not in existing.get(key.replace('-', '_').replace(' ', '_')):
                         commands.append('{0} {1}'.format(key, target))
                 else:
                     commands.append('{0} {1}'.format(key, target))
+
+            if existing.get(key.replace('-', '_').replace(' ', '_')):
+                for exi in existing.get(key.replace('-', '_').replace(' ', '_')):
+                    if exi not in value:
+                        commands.append('no {0} {1}'.format(key, exi))
+
         elif value == 'default':
             existing_value = existing_commands.get(key)
             if existing_value:
@@ -273,24 +281,6 @@ def main():
         commands, parents = state_absent(module, existing, proposed)
 
     if commands:
-        if (existing.get('route_distinguisher') and
-                proposed.get('route_distinguisher')):
-            if (existing['route_distinguisher'] != proposed['route_distinguisher'] and
-                    proposed['route_distinguisher'] != 'default'):
-                warnings.append('EVPN RD {0} was automatically removed. '
-                                'It is highly recommended to use a task '
-                                '(with default as value) to explicitly '
-                                'unconfigure it.'.format(existing['route_distinguisher']))
-                remove_commands = ['no rd {0}'.format(existing['route_distinguisher'])]
-
-                candidate = CustomNetworkConfig(indent=3)
-                candidate.add(remove_commands, parents=parents)
-                load_config(module, candidate)
-                results['changed'] = True
-                results['commands'] = candidate.items_text()
-                time.sleep(30)
-
-        else:
             candidate = CustomNetworkConfig(indent=3)
             candidate.add(commands, parents=parents)
             candidate = candidate.items_text()


Test patch:
diff --git a/test/integration/targets/nxos_evpn_vni/tests/common/sanity.yaml b/test/integration/targets/nxos_evpn_vni/tests/common/sanity.yaml
--- a/test/integration/targets/nxos_evpn_vni/tests/common/sanity.yaml
+++ b/test/integration/targets/nxos_evpn_vni/tests/common/sanity.yaml
@@ -28,9 +28,13 @@
       vni: 6000
       route_distinguisher: "60:10"
       route_target_import:
+          - auto
           - "5000:10"
           - "4100:100"
-      route_target_export: auto
+      route_target_export:
+          - auto
+          - "5000:10"
+          - "1.1.1.1:43"
       provider: "{{ connection }}"
     register: result
 
@@ -46,6 +50,40 @@
       that:
         - "result.changed == false"
 
+  - name: "Configure nxos_evpn_vni"
+    nxos_evpn_vni: &evpn_vni1
+      vni: 6000
+      route_distinguisher: "50:20"
+      route_target_import: auto
+      route_target_export: auto
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
+
+  - name: "Check Idempotence"
+    nxos_evpn_vni: *evpn_vni1
+    register: result
+
+  - assert: *false
+
+  - name: "Configure nxos_evpn_vni"
+    nxos_evpn_vni: &evpn_vni_def
+      vni: 6000
+      route_distinguisher: default
+      route_target_import: default
+      route_target_export: default
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
+
+  - name: "Check Idempotence"
+    nxos_evpn_vni: *evpn_vni_def
+    register: result
+
+  - assert: *false
+
   - name: "remove nxos_evpn_vni"
     nxos_evpn_vni: &rvni
       vni: 6000
@@ -61,6 +99,8 @@
 
   - assert: *false
 
+  when: not (platform is search('N3K')) and not (platform is match('N35'))
+
   always:
   - name: "Remove nv overlay evpn"
     nxos_config: *remove_evpn
diff --git a/test/units/modules/network/nxos/test_nxos_evpn_vni.py b/test/units/modules/network/nxos/test_nxos_evpn_vni.py
--- a/test/units/modules/network/nxos/test_nxos_evpn_vni.py
+++ b/test/units/modules/network/nxos/test_nxos_evpn_vni.py
@@ -57,7 +57,8 @@ def test_nxos_evpn_vni_present(self):
         result = self.execute_module(changed=True)
         self.assertEqual(result['commands'], ['evpn',
                                               'vni 6000 l2',
-                                              'route-target import 5000:10'])
+                                              'route-target import 5000:10',
+                                              'no route-target import auto'])
 
     def test_nxos_evpn_vni_absent_not_existing(self):
         set_module_args(dict(vni='12000', state='absent'))