Repository: ansible/ansible

Issue:
nxos_vrrp 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_vrrp

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.6.0 (devel fed20b825f) last updated 2018/02/15 12:51:12 (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 few problems with nxos_vrrp module.
1. The default for admin_state is shown as 'no shutdown', however the actual default is 'shutdown'
2. There is no support for setting parameters to default using 'default' keyword. The only way to remove some parameters is to remove the entire vrrp group which is not desirable.
3. There is no support for 'advertisement-interval' parameter.

Note: I will open a PR shortly to address these above issues


Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_vrrp.py b/lib/ansible/modules/network/nxos/nxos_vrrp.py
--- a/lib/ansible/modules/network/nxos/nxos_vrrp.py
+++ b/lib/ansible/modules/network/nxos/nxos_vrrp.py
@@ -49,31 +49,38 @@
         description:
             - Full name of interface that is being managed for VRRP.
         required: true
+    interval:
+        description:
+            - Time interval between advertisement or 'default' keyword
+        required: false
+        default: 1
+        version_added: 2.6
     priority:
         description:
-            - VRRP priority.
+            - VRRP priority or 'default' keyword
         required: false
-        default: null
+        default: 100
     preempt:
         description:
             - Enable/Disable preempt.
         choices: ['True', 'False']
+        default: True
     vip:
         description:
-            - VRRP virtual IP address.
+            - VRRP virtual IP address or 'default' keyword
         required: false
         default: null
     authentication:
         description:
-            - Clear text authentication string.
+            - Clear text authentication string or 'default' keyword
         required: false
         default: null
     admin_state:
         description:
             - Used to enable or disable the VRRP process.
         required: false
-        choices: ['shutdown', 'no shutdown']
-        default: no shutdown
+        choices: ['shutdown', 'no shutdown', 'default']
+        default: shutdown
     state:
         description:
             - Specify desired state of the resource.
@@ -121,6 +128,14 @@
 from ansible.module_utils.basic import AnsibleModule
 
 
+PARAM_TO_DEFAULT_KEYMAP = {
+    'priority': '100',
+    'interval': '1',
+    'vip': '0.0.0.0',
+    'admin_state': 'shutdown',
+}
+
+
 def execute_show_command(command, module):
     if 'show run' not in command:
         output = 'json'
@@ -253,12 +268,12 @@ def get_existing_vrrp(interface, group, module, name):
 
         if parsed_vrrp['group'] == group:
             parsed_vrrp['admin_state'] = get_vrr_status(group, module, name)
-
             return parsed_vrrp
+
     return vrrp
 
 
-def get_commands_config_vrrp(delta, group):
+def get_commands_config_vrrp(delta, existing, group):
     commands = []
 
     CMDS = {
@@ -266,32 +281,34 @@ def get_commands_config_vrrp(delta, group):
         'preempt': 'preempt',
         'vip': 'address {0}',
         'interval': 'advertisement-interval {0}',
-        'auth': 'authentication text {0}'
+        'auth': 'authentication text {0}',
+        'admin_state': '{0}',
     }
 
-    vip = delta.get('vip')
-    priority = delta.get('priority')
+    for arg in ['vip', 'priority', 'interval', 'admin_state']:
+        val = delta.get(arg)
+        if val == 'default':
+            val = PARAM_TO_DEFAULT_KEYMAP.get(arg)
+            if val != existing.get(arg):
+                commands.append((CMDS.get(arg)).format(val))
+        elif val:
+            commands.append((CMDS.get(arg)).format(val))
+
     preempt = delta.get('preempt')
-    interval = delta.get('interval')
     auth = delta.get('authentication')
-    admin_state = delta.get('admin_state')
 
-    if vip:
-        commands.append((CMDS.get('vip')).format(vip))
-    if priority:
-        commands.append((CMDS.get('priority')).format(priority))
     if preempt:
         commands.append(CMDS.get('preempt'))
     elif preempt is False:
         commands.append('no ' + CMDS.get('preempt'))
-    if interval:
-        commands.append((CMDS.get('interval')).format(interval))
     if auth:
-        commands.append((CMDS.get('auth')).format(auth))
-    if admin_state:
-        commands.append(admin_state)
+        if auth != 'default':
+            commands.append((CMDS.get('auth')).format(auth))
+        elif existing.get('authentication'):
+            commands.append('no authentication')
 
-    commands.insert(0, 'vrrp {0}'.format(group))
+    if commands:
+        commands.insert(0, 'vrrp {0}'.format(group))
 
     return commands
 
@@ -329,12 +346,13 @@ def main():
     argument_spec = dict(
         group=dict(required=True, type='str'),
         interface=dict(required=True),
+        interval=dict(required=False, type='str'),
         priority=dict(required=False, type='str'),
         preempt=dict(required=False, type='bool'),
         vip=dict(required=False, type='str'),
         admin_state=dict(required=False, type='str',
-                         choices=['shutdown', 'no shutdown'],
-                         default='no shutdown'),
+                         choices=['shutdown', 'no shutdown', 'default'],
+                         default='shutdown'),
         authentication=dict(required=False, type='str'),
         state=dict(choices=['absent', 'present'], required=False, default='present')
     )
@@ -349,6 +367,7 @@ def main():
     interface = module.params['interface'].lower()
     group = module.params['group']
     priority = module.params['priority']
+    interval = module.params['interval']
     preempt = module.params['preempt']
     vip = module.params['vip']
     authentication = module.params['authentication']
@@ -375,7 +394,7 @@ def main():
                              'a layer 3 port first.', interface=interface)
 
     args = dict(group=group, priority=priority, preempt=preempt,
-                vip=vip, authentication=authentication,
+                vip=vip, authentication=authentication, interval=interval,
                 admin_state=admin_state)
 
     proposed = dict((k, v) for k, v in args.items() if v is not None)
@@ -389,8 +408,9 @@ def main():
         delta = dict(
             set(proposed.items()).difference(existing.items()))
         if delta:
-            command = get_commands_config_vrrp(delta, group)
-            commands.append(command)
+            command = get_commands_config_vrrp(delta, existing, group)
+            if command:
+                commands.append(command)
     elif state == 'absent':
         if existing:
             commands.append(['no vrrp {0}'.format(group)])


Test patch:
diff --git a/test/integration/targets/nxos_vrrp/tests/common/sanity.yaml b/test/integration/targets/nxos_vrrp/tests/common/sanity.yaml
--- a/test/integration/targets/nxos_vrrp/tests/common/sanity.yaml
+++ b/test/integration/targets/nxos_vrrp/tests/common/sanity.yaml
@@ -26,7 +26,7 @@
       interface: vlan10
       group: 100
       vip: 10.1.100.1
-      admin_state: shutdown
+      admin_state: 'no shutdown'
       provider: "{{ connection }}"
     register: result
 
@@ -42,13 +42,27 @@
       that:
         - "result.changed == false"
 
+  - name: Ensure vrrp group 100 is default
+    nxos_vrrp: &configure_def
+      interface: vlan10
+      group: 100
+      vip: default
+      admin_state: default
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
+
+  - name: "Conf Idempotence"
+    nxos_vrrp: *configure_def
+    register: result
+
+  - assert: *false
+
   - name: Ensure removal of the vrrp group config
-  # vip is required to ensure the user knows what they are removing
     nxos_vrrp: &remove
       interface: vlan10
       group: 100
-      vip: 10.1.100.1
-      admin_state: shutdown
       state: absent
       provider: "{{ connection }}"
     register: result
@@ -72,7 +86,8 @@
       interface: vlan10
       group: 100
       vip: 10.1.100.1
-      preempt: false
+      preempt: False
+      interval: 10
       priority: 130
       authentication: AUTHKEY
       provider: "{{ connection }}"
@@ -86,6 +101,26 @@
 
   - assert: *false
 
+  - name: Re-config with defaults
+    nxos_vrrp: &reconfig_def
+      interface: vlan10
+      group: 100
+      vip: default
+      preempt: True
+      interval: default
+      priority: default
+      authentication: default
+      provider: "{{ connection }}"
+    register: result
+
+  - assert: *true
+
+  - name: "Reconfig Idempotence"
+    nxos_vrrp: *reconfig_def
+    register: result
+
+  - assert: *false
+
   always:
     - name: remove vrrp
       nxos_vrrp: *remove