Repository: ansible/ansible

Issue:
nxos_vxlan_vtep_vni default issues
<!---
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 or feature
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
nxos_vxlan_vtep_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)I7(1)
##### 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.
-->
There are multiple issues with nxos_vxlan_vtep_vni module.

1. Documentation error where default choice for ingress_replication parameter is missing
2. If we set default for ingress_replication, multicast_group or peer_list, failures are occuring. The only way to remove them is to remove the entire vni member.
3. Cannot change the ingress_replication from static to bgp or vice versa on some versions of n9k.
4. We can create a new member vni with the parameters but cannot add some parameters to an already existing group.

Note: I will open a PR shortly to address these above issues.




Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_vxlan_vtep_vni.py b/lib/ansible/modules/network/nxos/nxos_vxlan_vtep_vni.py
--- a/lib/ansible/modules/network/nxos/nxos_vxlan_vtep_vni.py
+++ b/lib/ansible/modules/network/nxos/nxos_vxlan_vtep_vni.py
@@ -56,7 +56,7 @@
     description:
       - Specifies mechanism for host reachability advertisement.
     required: false
-    choices: ['bgp','static']
+    choices: ['bgp','static', 'default']
     default: null
   multicast_group:
     description:
@@ -110,6 +110,11 @@
     'assoc_vrf',
     'suppress_arp',
 ]
+PARAM_TO_DEFAULT_KEYMAP = {
+    'multicast_group': '',
+    'peer_list': [],
+    'ingress_replication': '',
+}
 PARAM_TO_COMMAND_KEYMAP = {
     'assoc_vrf': 'associate-vrf',
     'interface': 'interface',
@@ -203,19 +208,33 @@ def state_present(module, existing, proposed, candidate):
                 command = 'no {0}'.format(command)
             commands.append(command)
 
-        elif key == 'peer-ip' and value != 'default':
+        elif key == 'peer-ip' and value != []:
             for peer in value:
                 commands.append('{0} {1}'.format(key, peer))
 
         elif key == 'mcast-group' and value != existing_commands.get(key):
             commands.append('no {0}'.format(key))
-            commands.append('{0} {1}'.format(key, value))
+            vni_command = 'member vni {0}'.format(module.params['vni'])
+            if vni_command not in commands:
+                commands.append('member vni {0}'.format(module.params['vni']))
+            if value != PARAM_TO_DEFAULT_KEYMAP.get('multicast_group', 'default'):
+                commands.append('{0} {1}'.format(key, value))
+
+        elif key == 'ingress-replication protocol' and value != existing_commands.get(key):
+            evalue = existing_commands.get(key)
+            dvalue = PARAM_TO_DEFAULT_KEYMAP.get('ingress_replication', 'default')
+            if value != dvalue:
+                if evalue != dvalue:
+                    commands.append('no {0} {1}'.format(key, evalue))
+                commands.append('{0} {1}'.format(key, value))
+            else:
+                commands.append('no {0} {1}'.format(key, evalue))
 
         elif value is True:
             commands.append(key)
         elif value is False:
             commands.append('no {0}'.format(key))
-        elif value == 'default':
+        elif value == 'default' or value == []:
             if existing_commands.get(key):
                 existing_value = existing_commands.get(key)
                 if key == 'peer-ip':
@@ -232,15 +251,26 @@ def state_present(module, existing, proposed, candidate):
 
     if commands:
         vni_command = 'member vni {0}'.format(module.params['vni'])
-        ingress_replication_command = 'ingress-replication protocol static'
+        ingress_replications_command = 'ingress-replication protocol static'
+        ingress_replicationb_command = 'ingress-replication protocol bgp'
+        ingress_replicationns_command = 'no ingress-replication protocol static'
+        ingress_replicationnb_command = 'no ingress-replication protocol bgp'
         interface_command = 'interface {0}'.format(module.params['interface'])
 
-        if ingress_replication_command in commands:
+        if any(c in commands for c in (ingress_replications_command, ingress_replicationb_command,
+               ingress_replicationnb_command, ingress_replicationns_command)):
             static_level_cmds = [cmd for cmd in commands if 'peer' in cmd]
-            parents = [interface_command, vni_command, ingress_replication_command]
+            parents = [interface_command, vni_command]
+            for cmd in commands:
+                parents.append(cmd)
             candidate.add(static_level_cmds, parents=parents)
             commands = [cmd for cmd in commands if 'peer' not in cmd]
 
+        elif 'peer-ip' in commands[0]:
+            static_level_cmds = [cmd for cmd in commands]
+            parents = [interface_command, vni_command, ingress_replications_command]
+            candidate.add(static_level_cmds, parents=parents)
+
         if vni_command in commands:
             parents = [interface_command]
             commands.remove(vni_command)
@@ -288,7 +318,7 @@ def main():
                 module.fail_json(msg='assoc_vrf cannot be used with '
                                      '{0} param'.format(param))
     if module.params['peer_list']:
-        if module.params['ingress_replication'] != 'static':
+        if module.params['peer_list'][0] != 'default' and module.params['ingress_replication'] != 'static':
             module.fail_json(msg='ingress_replication=static is required '
                                  'when using peer_list param')
         else:
@@ -322,6 +352,9 @@ def main():
 
     proposed = {}
     for key, value in proposed_args.items():
+        if key in ['multicast_group', 'peer_list', 'ingress_replication']:
+            if str(value).lower() == 'default':
+                value = PARAM_TO_DEFAULT_KEYMAP.get(key, 'default')
         if key != 'interface' and existing.get(key) != value:
             proposed[key] = value
 


Test patch:
diff --git a/test/integration/targets/nxos_vxlan_vtep_vni/tests/common/sanity.yaml b/test/integration/targets/nxos_vxlan_vtep_vni/tests/common/sanity.yaml
--- a/test/integration/targets/nxos_vxlan_vtep_vni/tests/common/sanity.yaml
+++ b/test/integration/targets/nxos_vxlan_vtep_vni/tests/common/sanity.yaml
@@ -49,8 +49,17 @@
       state: absent
       provider: "{{ connection }}"
 
-  - name: configure vxlan_vtep_vni mcast
+  - name: configure vxlan_vtep_vni
     nxos_vxlan_vtep_vni: &conf2
+      interface: nve1
+      vni: 8000
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
+
+  - name: configure vxlan_vtep_vni mcast
+    nxos_vxlan_vtep_vni: &conf3
       interface: nve1
       vni: 8000
       multicast_group: 224.1.1.1
@@ -59,8 +68,24 @@
 
   - assert: *true
 
-  - name: "Conf 2 Idempotence"
-    nxos_vxlan_vtep_vni: *conf2
+  - name: "Conf 3 Idempotence"
+    nxos_vxlan_vtep_vni: *conf3
+    register: result
+
+  - assert: *false
+
+  - name: configure vxlan_vtep_vni default mcast
+    nxos_vxlan_vtep_vni: &conf4
+      interface: nve1
+      vni: 8000
+      multicast_group: default
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
+
+  - name: "Conf 4 Idempotence"
+    nxos_vxlan_vtep_vni: *conf4
     register: result
 
   - assert: *false
@@ -79,8 +104,65 @@
       provider: "{{ connection }}"
 
   - block:
+    - name: configure vxlan_vtep_vni
+      nxos_vxlan_vtep_vni: &conf5
+        interface: nve1
+        vni: 8000
+        provider: "{{ connection }}"
+      register: result
+  
+    - assert: *true
+
+    - name: configure vxlan_vtep_vni ingress static
+      nxos_vxlan_vtep_vni: &conf6
+        interface: nve1
+        vni: 8000
+        ingress_replication: static
+        provider: "{{ connection }}"
+      register: result
+  
+    - assert: *true
+
+    - name: "Conf 6 Idempotence"
+      nxos_vxlan_vtep_vni: *conf6
+      register: result
+
+    - assert: *false
+
+    - name: configure vxlan_vtep_vni ingress bgp
+      nxos_vxlan_vtep_vni: &conf7
+        interface: nve1
+        vni: 8000
+        ingress_replication: bgp
+        provider: "{{ connection }}"
+      register: result
+  
+    - assert: *true
+
+    - name: "Conf 7 Idempotence"
+      nxos_vxlan_vtep_vni: *conf7
+      register: result
+
+    - assert: *false
+
+    - name: remove ingress_repl
+      nxos_vxlan_vtep_vni: &conf8
+        interface: nve1
+        vni: 8000
+        ingress_replication: default
+        provider: "{{ connection }}"
+      register: result
+  
+    - assert: *true
+
+    - name: "Conf 8 Idempotence"
+      nxos_vxlan_vtep_vni: *conf8
+      register: result
+
+    - assert: *false
+
     - name: configure vxlan_vtep_vni peer-list
-      nxos_vxlan_vtep_vni: &conf3
+      nxos_vxlan_vtep_vni: &conf9
         interface: nve1
         vni: 8000
         peer_list: 
@@ -94,12 +176,35 @@
   
     - assert: *true
 
-    - name: "Conf 3 Idempotence"
-      nxos_vxlan_vtep_vni: *conf3
+    - name: "Conf 9 Idempotence"
+      nxos_vxlan_vtep_vni: *conf9
       register: result
   
     - assert: *false
 
+    - name: configure vxlan_vtep_vni default peer-list
+      nxos_vxlan_vtep_vni: &conf10
+        interface: nve1
+        vni: 8000
+        peer_list: default
+        ingress_replication: static
+        provider: "{{ connection }}"
+      register: result
+  
+    - assert: *true
+
+    - name: "Conf 10 Idempotence"
+      nxos_vxlan_vtep_vni: *conf10
+      register: result
+  
+    - assert: *false
+
+    - name: "Conf 9 again"
+      nxos_vxlan_vtep_vni: *conf9
+      register: result
+  
+    - assert: *true
+
     - name: remove vxlan_vtep_vni
       nxos_vxlan_vtep_vni: *remove
       register: result