Repository: ansible/ansible

Issue:
parse_cli could use better error message(s)
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
parse_cli

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible-playbook 2.5.0 (devel 33b8d7069f) last updated 2017/09/18 13:18:04 (GMT +000)
  config file = None
  configured module search path = [u'/home/vagrant/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/vagrant/ansible/lib/ansible
  executable location = /home/vagrant/ansible/bin/ansible-playbook
  python version = 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4]
```

##### OS / ENVIRONMENT
N/A

##### SUMMARY
parse_cli fails with a generic error message when called with a list argument (the value of registered **stdout** key) instead of a string argument (because one should use **stdout[0]**)

##### STEPS TO REPRODUCE
Sample playbook:
```- name: Retrieve OSPF neighbor information
  hosts: E1
  connection: local
  tasks:
  - ios_command:
      commands: [ show ip ospf neighbor ]
    register: ospf_printout

  - debug: msg={{ospf_printout.stdout}}
  - debug: msg={{ ospf_printout.stdout | parse_cli('../cli_filters/ios_ospf_neighbor.yml') }}
```

##### EXPECTED RESULTS
Jinja2 expression in the last **debug** is wrong. It should use **stdout[0]** instead of **stdout**. However, this is a common mistake and I would like to see parse_cli say "parse_cli works on strings, not on lists of strings"

##### ACTUAL RESULTS
```
TASK [ios_command] *********************************************************************************
ok: [E1]

TASK [debug] ***************************************************************************************
ok: [E1] => {
    "msg": [
        "Neighbor ID     Pri   State           Dead Time   Address         Interface\n192.168.0.4       0   FULL/  -        00:00:09    10.0.0.22       GigabitEthernet0/2\n192.168.0.1       0   FULL/  -        00:00:09    10.0.0.14       GigabitEthernet0/1"
    ]
}

TASK [debug] ***************************************************************************************
fatal: [E1]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ ospf_printout.stdout | parse_cli('../cli_filters/ios_ospf_neighbor.yml') }}): expected string or buffer"}
```



Discussion:
I agree I was hitting the same error just like that today and adding [0] after stdout fixed that.


Resolving patch:
diff --git a/lib/ansible/plugins/filter/network.py b/lib/ansible/plugins/filter/network.py
--- a/lib/ansible/plugins/filter/network.py
+++ b/lib/ansible/plugins/filter/network.py
@@ -27,7 +27,7 @@
 from collections import Mapping
 
 from ansible.module_utils.network_common import Template
-from ansible.module_utils.six import iteritems
+from ansible.module_utils.six import iteritems, string_types
 from ansible.errors import AnsibleError
 
 try:
@@ -76,6 +76,12 @@ def re_search(regex, value):
 
 
 def parse_cli(output, tmpl):
+    if not isinstance(output, string_types):
+        raise AnsibleError("parse_cli input should be a string, but was given a input of %s" % (type(output)))
+
+    if not os.path.exists(tmpl):
+        raise AnsibleError('unable to locate parse_cli template: %s' % tmpl)
+
     try:
         template = Template()
     except ImportError as exc:
@@ -216,8 +222,11 @@ def parse_cli_textfsm(value, template):
     if not HAS_TEXTFSM:
         raise AnsibleError('parse_cli_textfsm filter requires TextFSM library to be installed')
 
+    if not isinstance(value, string_types):
+        raise AnsibleError("parse_cli_textfsm input should be a string, but was given a input of %s" % (type(value)))
+
     if not os.path.exists(template):
-        raise AnsibleError('unable to locate parse_cli template: %s' % template)
+        raise AnsibleError('unable to locate parse_cli_textfsm template: %s' % template)
 
     try:
         template = open(template)


Test patch: