Repository: ansible/ansible

Issue:
nxos_ntp_options 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.
Always add information AFTER of these html comments.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
Insert below this comment the 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_ntp_options
##### 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)I2(5)
##### 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 some issues with nxos_ntp_options module.

1. Documentation says that when the state is absent, the parameters will be flipped which is wrong. This should be just that, the parameters will be set to 'False'. The code is correct here though!
2. There is no way to turn off logging or master parameters individually, if we set the state to absent, all of them will be reset.
3. if we do not specify stratum in the playbook task, it will be reset to default which is incorrect.
4. For getting the configuration, the code is calling "show running" without any filters and this results in parsing a very large output and it is very inefficient.
5. Unnecessary range checks which should be left to platform.

Note: I will open a PR shortly to address the above issues

nxos_ntp_options 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.
Always add information AFTER of these html comments.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
Insert below this comment the 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_ntp_options
##### 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)I2(5)
##### 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 some issues with nxos_ntp_options module.

1. Documentation says that when the state is absent, the parameters will be flipped which is wrong. This should be just that, the parameters will be set to 'False'. The code is correct here though!
2. There is no way to turn off logging or master parameters individually, if we set the state to absent, all of them will be reset.
3. if we do not specify stratum in the playbook task, it will be reset to default which is incorrect.
4. For getting the configuration, the code is calling "show running" without any filters and this results in parsing a very large output and it is very inefficient.
5. Unnecessary range checks which should be left to platform.

Note: I will open a PR shortly to address the above issues



Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_ntp_options.py b/lib/ansible/modules/network/nxos/nxos_ntp_options.py
--- a/lib/ansible/modules/network/nxos/nxos_ntp_options.py
+++ b/lib/ansible/modules/network/nxos/nxos_ntp_options.py
@@ -34,12 +34,8 @@
     - Jason Edelman (@jedelman8)
 notes:
     - Tested against NXOSv 7.3.(0)D1(1) on VIRL
-    - At least one of C(master) or C(logging) params must be supplied.
-    - When C(state=absent), boolean parameters are flipped,
-      e.g. C(master=true) will disable the authoritative server.
-    - When C(state=absent) and C(master=true), the stratum will be removed as well.
-    - When C(state=absent) and C(master=false), the stratum will be configured
-      to its default value, 8.
+    - When C(state=absent), master and logging will be set to False and
+      stratum will be removed as well
 options:
     master:
         description:
@@ -75,7 +71,7 @@
     description: command sent to the device
     returned: always
     type: list
-    sample: ["no ntp logging", "ntp master 11"]
+    sample: ["no ntp logging", "ntp master 12"]
 '''
 import re
 
@@ -85,20 +81,20 @@
 
 
 def get_current(module):
-    cmd = ('show running-config', 'show ntp logging')
+    cmd = ('show running-config | inc ntp')
 
-    output = run_commands(module, ({'command': cmd[0], 'output': 'text'},
-                                   {'command': cmd[1], 'output': 'text'}))
+    master = False
+    logging = False
+    stratum = None
 
-    match = re.search(r"^ntp master(?: (\d+))", output[0], re.M)
-    if match:
-        master = True
-        stratum = match.group(1)
-    else:
-        master = False
-        stratum = None
+    output = run_commands(module, ({'command': cmd, 'output': 'text'}))[0]
 
-    logging = 'enabled' in output[1].lower()
+    if output:
+        match = re.search(r"^ntp master(?: (\d+))", output, re.M)
+        if match:
+            master = True
+            stratum = match.group(1)
+        logging = 'ntp logging' in output.lower()
 
     return {'master': master, 'stratum': stratum, 'logging': logging}
 
@@ -106,7 +102,7 @@ def get_current(module):
 def main():
     argument_spec = dict(
         master=dict(required=False, type='bool'),
-        stratum=dict(required=False, type='str', default='8'),
+        stratum=dict(required=False, type='str'),
         logging=dict(required=False, type='bool'),
         state=dict(choices=['absent', 'present'], default='present'),
     )
@@ -124,15 +120,10 @@ def main():
     logging = module.params['logging']
     state = module.params['state']
 
-    if stratum:
-        try:
-            stratum_int = int(stratum)
-            if stratum_int < 1 or stratum_int > 15:
-                raise ValueError
-        except ValueError:
-            module.fail_json(msg='stratum must be an integer between 1 and 15')
+    if stratum and master is False:
+        if stratum != 8:
+            module.fail_json(msg='master MUST be True when stratum is changed')
 
-    desired = {'master': master, 'stratum': stratum, 'logging': logging}
     current = get_current(module)
 
     result = {'changed': False}
@@ -146,19 +137,17 @@ def main():
             commands.append('no ntp logging')
 
     elif state == 'present':
-        if desired['master'] and desired['master'] != current['master']:
-            if desired['stratum']:
-                commands.append('ntp master %s' % stratum)
-            else:
-                commands.append('ntp master')
-        elif desired['stratum'] and desired['stratum'] != current['stratum']:
+        if master and not current['master']:
+            commands.append('ntp master')
+        elif master is False and current['master']:
+            commands.append('no ntp master')
+        if stratum and stratum != current['stratum']:
             commands.append('ntp master %s' % stratum)
 
-        if desired['logging'] and desired['logging'] != current['logging']:
-            if desired['logging']:
-                commands.append('ntp logging')
-            else:
-                commands.append('no ntp logging')
+        if logging and not current['logging']:
+            commands.append('ntp logging')
+        elif logging is False and current['logging']:
+            commands.append('no ntp logging')
 
     result['commands'] = commands
     result['updates'] = commands


Test patch:
diff --git a/test/integration/targets/nxos_ntp_options/tests/common/sanity.yaml b/test/integration/targets/nxos_ntp_options/tests/common/sanity.yaml
--- a/test/integration/targets/nxos_ntp_options/tests/common/sanity.yaml
+++ b/test/integration/targets/nxos_ntp_options/tests/common/sanity.yaml
@@ -31,43 +31,73 @@
       that:
         - "result.changed == false"
 
-  - name: Remove ntp with master and default stratum
-    nxos_ntp_options: &remove_master_default_stratum
-      logging: true
+  - name: Configure ntp with master and non-default stratum
+    nxos_ntp_options: &configure_master_non_default_stratum
       master: true
-      state: absent
+      stratum: 10
+      state: present
       provider: "{{ connection }}"      
     register: result
 
   - assert: *true
 
-  - name: Configure ntp with master and non-default stratum
-    nxos_ntp_options: &configure_master_non_default_stratum
+  - name: "Check Idempotence - Configure ntp with master and non-default stratum"
+    nxos_ntp_options: *configure_master_non_default_stratum
+    register: result
+
+  - assert: *false
+
+  - name: Configure ntp with master and no logging
+    nxos_ntp_options: &configure_no_log
       master: true
-      logging: true
       stratum: 10
+      logging: false
       state: present
-      provider: "{{ connection }}"      
+      provider: "{{ connection }}"
     register: result
 
   - assert: *true
 
-  - name: "Check Idempotence - Configure ntp with master and non-default stratum"
-    nxos_ntp_options: *configure_master_non_default_stratum
+  - name: "Check Idempotence - Configure ntp with master and no logging"
+    nxos_ntp_options: *configure_no_log
     register: result
 
   - assert: *false
 
-  - name: Remove ntp with master and non-default stratum
-    nxos_ntp_options: &remove_master_non_default_stratum
+  - name: Configure ntp with logging and no master
+    nxos_ntp_options: &configure_no_master
+      master: false
       logging: true
-      master: true
-      state: absent
+      state: present
       provider: "{{ connection }}"
     register: result
 
   - assert: *true
 
+  - name: "Check Idempotence - Configure ntp with logging and no master"
+    nxos_ntp_options: *configure_no_master
+    register: result
+
+  - assert: *false
+
+  - name: "Configure ntp with master and non-default stratum again"
+    nxos_ntp_options: *configure_master_non_default_stratum
+    register: result
+
+  - assert: *true
+
+  - name: Remove ntp options
+    nxos_ntp_options: *default
+    register: result
+
+  - assert: *true
+
+  - name: "Check Idempotence - Remove"
+    nxos_ntp_options: *default
+    register: result
+
+  - assert: *false
+
   always:
   - name: Cleanup ntp config
     nxos_ntp_options: *default
diff --git a/test/sanity/validate-modules/ignore.txt b/test/sanity/validate-modules/ignore.txt
--- a/test/sanity/validate-modules/ignore.txt
+++ b/test/sanity/validate-modules/ignore.txt
@@ -1347,7 +1347,6 @@ lib/ansible/modules/network/nxos/nxos_igmp_interface.py E326
 lib/ansible/modules/network/nxos/nxos_interface.py E324
 lib/ansible/modules/network/nxos/nxos_lldp.py E326
 lib/ansible/modules/network/nxos/nxos_logging.py E325
-lib/ansible/modules/network/nxos/nxos_ntp_options.py E324
 lib/ansible/modules/network/nxos/nxos_nxapi.py E324
 lib/ansible/modules/network/nxos/nxos_nxapi.py E325
 lib/ansible/modules/network/nxos/nxos_nxapi.py E326