Repository: ansible/ansible

Issue:
ios_config: multi-line command failure
<!---
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
 - Bug Report
##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
ios_config
##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4]
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
No alterations 
##### 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.
-->
N/A
##### SUMMARY
<!--- Explain the problem briefly -->
When attempting to use commands which require multiple lines of input the command fails.
##### 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.
-->
Manually create a crypto trustpoint on the router:

crypto pki trustpoint TP_TEST

Then use ansible to remove the trustpoint:
```
  - name: Remove old Trustpoint
    ios_config:
     provider: "{{ provider }}"
     match: none
     lines:
     - "no crypto pki trustpoint TP_TEST"
```
This will produce the following error:

    "msg": "no crypto pki trustpoint TP_TEST\r\n% Removing an enrolled trustpoint will destroy all certificates\r\n received from the related Certificate Authority.\r\n\r\nAre you sure you want to do this? [yes/no]: ", 

This issue is also a problem when attempting to add certificates via the terminal using the following IOS command:

crypto pki import TP_TEST pem terminal password mypass

This command expects multiple lines of input following the initial command.

<!--- You can also paste gist.github.com links for larger files -->
YML:

```
---
- hosts: router
  gather_facts: no
  connection: local

  vars_prompt:
  - name: "username"
    prompt: "Username"
    private: no
  - name: "password"
    prompt: "Password"

  tasks:
  - name: Define Provider
    set_fact:
     provider:
      host: "{{ inventory_hostname }}"
      username: "{{ username }}"
      password: "{{ password }}"

  - name: Remove old Trustpoint
    ios_config:
     provider: "{{ provider }}"
     match: none
     lines: 
     - no crypto pki trustpoint TP_TEST
```
##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Expected capability to add multiple 'lines:' statements with the command as well as subsequent input. Even if by means of delimiters and string concatenation. Various combinations of attempts adding \n fails as the \r character is inserted seemingly automatically.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
root@dchidell-ubuntu:/home/david/vpn_ansible# ansible-playbook -i hosts show_fail.yml -vvv
Using /etc/ansible/ansible.cfg as config file

PLAYBOOK: show_fail.yml *********************************************************************************************************************
1 plays in show_fail.yml
Username: dchidell
Password: 

PLAY [router] *******************************************************************************************************************************
META: ran handlers

TASK [Define Provider] **********************************************************************************************************************
task path: /home/david/vpn_ansible/show_fail.yml:14
ok: [*********] => {
    "ansible_facts": {
        "provider": {
            "host": "*********", 
            "password": "********", 
            "username": "dchidell"
        }
    }, 
    "changed": false
}

TASK [Remove old Trustpoint] ****************************************************************************************************************
task path: /home/david/vpn_ansible/show_fail.yml:21
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/network/ios/ios_config.py
<*********> ESTABLISH LOCAL CONNECTION FOR USER: root
<*********> EXEC /bin/sh -c 'echo ~ && sleep 0'
<*********> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1492025425.15-41784346241163 `" && echo ansible-tmp-1492025425.15-41784346241163="` echo /root/.ansible/tmp/ansible-tmp-1492025425.15-41784346241163 `" ) && sleep 0'
<*********> PUT /tmp/tmp3kfzpW TO /root/.ansible/tmp/ansible-tmp-1492025425.15-41784346241163/ios_config.py
<*********> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1492025425.15-41784346241163/ /root/.ansible/tmp/ansible-tmp-1492025425.15-41784346241163/ios_config.py && sleep 0'
<*********> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1492025425.15-41784346241163/ios_config.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1492025425.15-41784346241163/" > /dev/null 2>&1 && sleep 0'
fatal: [*********]: FAILED! => {
    "changed": false, 
    "command": "no crypto pki trustpoint TP_TEST", 
    "failed": true, 
    "invocation": {
        "module_args": {
            "after": null, 
            "auth_pass": null, 
            "authorize": null, 
            "backup": false, 
            "before": null, 
            "config": null, 
            "defaults": false, 
            "force": false, 
            "host": null, 
            "lines": [
                "no crypto pki trustpoint TP_TEST"
            ], 
            "match": "none", 
            "multiline_delimiter": "@", 
            "parents": null, 
            "password": null, 
            "port": null, 
            "provider": {
                "auth_pass": null, 
                "authorize": null, 
                "host": "*******", 
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "port": null, 
                "ssh_keyfile": null, 
                "timeout": null, 
                "username": "dchidell"
            }, 
            "replace": "line", 
            "save": false, 
            "src": null, 
            "ssh_keyfile": null, 
            "timeout": null, 
            "username": null
        }
    }, 
    "msg": "no crypto pki trustpoint TP_TEST\r\n% Removing an enrolled trustpoint will destroy all certificates\r\n received from the related Certificate Authority.\r\n\r\nAre you sure you want to do this? [yes/no]: ", 
    "rc": 1
}
	to retry, use: --limit @/home/david/vpn_ansible/show_fail.retry

PLAY RECAP **********************************************************************************************************************************
*********                  : ok=1    changed=0    unreachable=0    failed=1   

root@dchidell-ubuntu:/home/david/vpn_ansible# 
```



Discussion:
cc @privateip @rcarrillocruz
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Hi,
Issue 3591 on Ansible module score in 2016 already stated that commands (such as "no username") requiring multilines input were not supported by ios_config. This is a major issue : 
I am evaluating Ansible for use by sensible organizations and this is really a pain.

I am waiting for 2.4.0 then, and meanwhile I'll use "ios_user" newly merged into ansible for the specific case of "no username" issue, but I really think that the whole "multilines" issue should be treated in 2.3.x if possible.

Best regards,
Eric Salomé
We will fix this on 2.5.
We have supporting code for handling prompts on IOS on the ios cliconf plugin.
We will migrate our modules to cliconf plugins on 2.5 and that feature will be implemented then.
cc @Qalthos @ganeshrn @gundalow @trishnaguha
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @kedarX
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/plugins/cliconf/ios.py b/lib/ansible/plugins/cliconf/ios.py
--- a/lib/ansible/plugins/cliconf/ios.py
+++ b/lib/ansible/plugins/cliconf/ios.py
@@ -19,6 +19,7 @@
 from __future__ import (absolute_import, division, print_function)
 __metaclass__ = type
 
+import ast
 import re
 import json
 
@@ -71,7 +72,7 @@ def get_config(self, source='running', flags=None):
     def edit_config(self, command):
         for cmd in chain(['configure terminal'], to_list(command), ['end']):
             try:
-                cmd = json.loads(cmd)
+                cmd = ast.literal_eval(cmd)
                 command = cmd['command']
                 prompt = cmd['prompt']
                 answer = cmd['answer']


Test patch: