Repository: ansible/ansible

Issue:
nxos_ntp: module fails if no NTP config is present
##### ISSUE TYPE
 - Bug Report


##### COMPONENT NAME
nxos_ntp

##### ANSIBLE VERSION

```
ansible 2.3.0 (devel 69c14bd003) last updated 2016/12/22 11:23:02 (GMT -400)
  config file = 
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
No special config

##### OS / ENVIRONMENT
NX-OS version 7.0(3)I2(4)

##### SUMMARY
Trying to add a new NTP server with no servers present results in a failure.

##### STEPS TO REPRODUCE
Trying to run this simple playbook, hosts n9k-tor group has host 192.168.2.11

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---

- name: gather int data
  hosts: n9k-tor 
  connection: local
  gather_facts: no


  tasks:

    - nxos_ntp:
        host: "{{ inventory_hostname }}"
        transport: cli 
        username: ***
        password: ****
        use_ssl: yes
        validate_certs: False
        server: 10.20.30.40 
        state: present

```


##### EXPECTED RESULTS
Server is added

##### ACTUAL RESULTS
The following error occurs:

```
An exception occurred during task execution. The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_B58nks/ansible_module_nxos_ntp.py", line 635, in <module>
    main()
  File "/tmp/ansible_B58nks/ansible_module_nxos_ntp.py", line 578, in main
    existing, peer_server_list = get_ntp_existing(address, peer_type, module)
  File "/tmp/ansible_B58nks/ansible_module_nxos_ntp.py", line 454, in get_ntp_existing
    peer_list = get_ntp_peer(module)
  File "/tmp/ansible_B58nks/ansible_module_nxos_ntp.py", line 403, in get_ntp_peer
    command, module, command_type='cli_show_ascii')[0]
IndexError: list index out of range

fatal: [192.168.2.11]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "invocation": {
        "module_name": "nxos_ntp"
    }, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_B58nks/ansible_module_nxos_ntp.py\", line 635, in <module>\n    main()\n  File \"/tmp/ansible_B58nks/ansible_module_nxos_ntp.py\", line 578, in main\n    existing, peer_server_list = get_ntp_existing(address, peer_type, module)\n  File \"/tmp/ansible_B58nks/ansible_module_nxos_ntp.py\", line 454, in get_ntp_existing\n    peer_list = get_ntp_peer(module)\n  File \"/tmp/ansible_B58nks/ansible_module_nxos_ntp.py\", line 403, in get_ntp_peer\n    command, module, command_type='cli_show_ascii')[0]\nIndexError: list index out of range\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE", 
    "rc": 0

```
##### QUICK FIX
I have discovered that changing lines 402-403 from
```
    ntp = execute_show_command(
command, module, command_type='cli_show_ascii')[0]
```
to
```
    ntp1 = execute_show_command( 
                command, module, command_type='cli_show_ascii')
    if len(ntp1) > 0:
      ntp = ntp1[0]
    else:
      return []

```
appears to fix the issue, but since I am quite new to programming, I am not sure whether it brakes any other functionality and how to test that properly.


Discussion:
cc @jedelman8, @GGabriele, @privateip, @gundalow, @Qalthos
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@eoprede https://github.com/ansible/ansible/pull/19678 should fix your problem when merged, which will happen this week
merged #19678 ... could you re-test and report if your issue is resolved?
@eoprede A fix has been integrated into `devel`. Could you please retest and let us know?

Thanks in advance.
needs_info
@gundalow 

Thanks, it does work with CLI transport. However using the nxapi transport I get the following (only when there's no NTP server present in config):

```
An exception occurred during task execution. The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_mFYrHl/ansible_module_nxos_ntp.py", line 637, in <module>
    main()
  File "/tmp/ansible_mFYrHl/ansible_module_nxos_ntp.py", line 580, in main
    existing, peer_server_list = get_ntp_existing(address, peer_type, module)
  File "/tmp/ansible_mFYrHl/ansible_module_nxos_ntp.py", line 456, in get_ntp_existing
    peer_list = get_ntp_peer(module)
  File "/tmp/ansible_mFYrHl/ansible_module_nxos_ntp.py", line 413, in get_ntp_peer
    split_ntp = ntp.splitlines()
AttributeError: 'dict' object has no attribute 'splitlines'

fatal: [test_n9k_2]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "invocation": {
        "module_name": "nxos_ntp"
    }, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_mFYrHl/ansible_module_nxos_ntp.py\", line 637, in <module>\n    main()\n  File \"/tmp/ansible_mFYrHl/ansible_module_nxos_ntp.py\", line 580, in main\n    existing, peer_server_list = get_ntp_existing(address, peer_type, module)\n  File \"/tmp/ansible_mFYrHl/ansible_module_nxos_ntp.py\", line 456, in get_ntp_existing\n    peer_list = get_ntp_peer(module)\n  File \"/tmp/ansible_mFYrHl/ansible_module_nxos_ntp.py\", line 413, in get_ntp_peer\n    split_ntp = ntp.splitlines()\nAttributeError: 'dict' object has no attribute 'splitlines'\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE", 
    "rc": 0
}
```

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_ntp.py b/lib/ansible/modules/network/nxos/nxos_ntp.py
--- a/lib/ansible/modules/network/nxos/nxos_ntp.py
+++ b/lib/ansible/modules/network/nxos/nxos_ntp.py
@@ -180,52 +180,56 @@ def get_ntp_source(module):
 def get_ntp_peer(module):
     command = 'show run | inc ntp.(server|peer)'
     ntp_peer_list = []
-    ntp = execute_show_command(
+    response = execute_show_command(
         command, module, command_type='cli_show_ascii')
-    if ntp:
-        ntp = ntp[0]
 
-        ntp_regex = (
-            ".*ntp\s(server\s(?P<address>\S+)|peer\s(?P<peer_address>\S+))"
-            "\s*((?P<prefer>prefer)\s*)?(use-vrf\s(?P<vrf_name>\S+)\s*)?"
-            "(key\s(?P<key_id>\d+))?.*"
+    if response:
+        if isinstance(response, list):
+            ntp = response[0]
+        else:
+            ntp = response
+        if ntp:
+            ntp_regex = (
+                ".*ntp\s(server\s(?P<address>\S+)|peer\s(?P<peer_address>\S+))"
+                "\s*((?P<prefer>prefer)\s*)?(use-vrf\s(?P<vrf_name>\S+)\s*)?"
+                "(key\s(?P<key_id>\d+))?.*"
             )
 
-        split_ntp = ntp.splitlines()
-        for peer_line in split_ntp:
-            ntp_peer = {}
-            try:
-                peer_address = None
-                vrf_name = None
-                prefer = None
-                key_id = None
-                match_ntp = re.match(ntp_regex, peer_line, re.DOTALL)
-                group_ntp = match_ntp.groupdict()
-
-                address = group_ntp["address"]
-                peer_address = group_ntp['peer_address']
-                prefer = group_ntp['prefer']
-                vrf_name = group_ntp['vrf_name']
-                key_id = group_ntp['key_id']
-
-                if prefer is not None:
-                    prefer = 'enabled'
-                else:
-                    prefer = 'disabled'
-
-                if address is not None:
-                    peer_type = 'server'
-                elif peer_address is not None:
-                    peer_type = 'peer'
-                    address = peer_address
-
-                args = dict(peer_type=peer_type, address=address, prefer=prefer,
-                            vrf_name=vrf_name, key_id=key_id)
-
-                ntp_peer = dict((k, v) for k, v in args.items())
-                ntp_peer_list.append(ntp_peer)
-            except AttributeError:
-                ntp_peer_list = []
+            split_ntp = ntp.splitlines()
+            for peer_line in split_ntp:
+                ntp_peer = {}
+                try:
+                    peer_address = None
+                    vrf_name = None
+                    prefer = None
+                    key_id = None
+                    match_ntp = re.match(ntp_regex, peer_line, re.DOTALL)
+                    group_ntp = match_ntp.groupdict()
+
+                    address = group_ntp["address"]
+                    peer_address = group_ntp['peer_address']
+                    prefer = group_ntp['prefer']
+                    vrf_name = group_ntp['vrf_name']
+                    key_id = group_ntp['key_id']
+
+                    if prefer is not None:
+                        prefer = 'enabled'
+                    else:
+                        prefer = 'disabled'
+
+                    if address is not None:
+                        peer_type = 'server'
+                    elif peer_address is not None:
+                        peer_type = 'peer'
+                        address = peer_address
+
+                    args = dict(peer_type=peer_type, address=address, prefer=prefer,
+                                vrf_name=vrf_name, key_id=key_id)
+
+                    ntp_peer = dict((k, v) for k, v in args.items())
+                    ntp_peer_list.append(ntp_peer)
+                except AttributeError:
+                    ntp_peer_list = []
 
     return ntp_peer_list
 
@@ -424,4 +428,3 @@ def main():
 from ansible.module_utils.basic import *
 if __name__ == '__main__':
     main()
-


Test patch: