Repository: ansible/ansible

Issue:
ios_template: running `show running-config all` in `configure` shell
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
ios_template

##### ANSIBLE VERSION
```
ansible 2.3.0 (test_ios_again e6d8111362) last updated 2017/02/14 18:47:58 (GMT +100)

```

##### CONFIGURATION

##### OS / ENVIRONMENT

##### SUMMARY

##### STEPS TO REPRODUCE
```yaml
- name: setup
  ios_config:
    commands:
      - no interface Loopback999
    match: none
  ignore_errors: yes 

- name: configure device with config
  ios_template:
    src: basic/config.j2
  register: result

```


##### EXPECTED RESULTS

##### ACTUAL RESULTS
```
TASK [ios_template : configure device with config] ***********************************************************************************************************************************************************************************************
task path: /home/johnb/git/ansible-inc/ansible/test/integration/targets/ios_template/tests/cli/basic.yaml:11
Using module file /home/johnb/git/ansible-inc/ansible/lib/ansible/modules/network/ios/_ios_template.py
<ios01> ESTABLISH LOCAL CONNECTION FOR USER: johnb
<ios01> EXEC /bin/sh -c 'echo ~ && sleep 0'
<ios01> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/johnb/.ansible/tmp/ansible-tmp-1487178250.65-147092922229605 `" && echo ansible-tmp-1487178250.65-147092922229605="` echo /home/johnb/.ansible/tmp/ansible-tmp-1487178250.65-147092922229605 `" ) && sleep 0'
<ios01> PUT /tmp/tmpRX27s9 TO /home/johnb/.ansible/tmp/ansible-tmp-1487178250.65-147092922229605/_ios_template.py
<ios01> EXEC /bin/sh -c 'chmod u+x /home/johnb/.ansible/tmp/ansible-tmp-1487178250.65-147092922229605/ /home/johnb/.ansible/tmp/ansible-tmp-1487178250.65-147092922229605/_ios_template.py && sleep 0'
<ios01> EXEC /bin/sh -c 'python /home/johnb/.ansible/tmp/ansible-tmp-1487178250.65-147092922229605/_ios_template.py; rm -rf "/home/johnb/.ansible/tmp/ansible-tmp-1487178250.65-147092922229605/" > /dev/null 2>&1 && sleep 0'
fatal: [ios01]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "invocation": {
        "module_args": {
            "auth_pass": null, 
            "authorize": null, 
            "backup": false, 
            "config": null, 
            "force": false, 
            "host": null, 
            "include_defaults": true, 
            "password": null, 
            "port": null, 
            "provider": null, 
            "src": "interface Loopback999\n description this is a test\n shutdown\n\n", 
            "ssh_keyfile": null, 
            "timeout": 10, 
            "username": null
        }
    }, 
    "msg": "unable to retrieve current config", 
    "stderr": "show running-config all\r\n                ^\r\n% Invalid input detected at '^' marker.\r\n\r\nios01(config)#", 
    "stderr_lines": [
        "show running-config all", 
        "                ^", 
        "% Invalid input detected at '^' marker.", 
        "", 
        "ios01(config)#"
    ]
}
```



Discussion:
cc @rcarrillocruz @Qalthos
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/module_utils/ios.py b/lib/ansible/module_utils/ios.py
--- a/lib/ansible/module_utils/ios.py
+++ b/lib/ansible/module_utils/ios.py
@@ -65,18 +65,19 @@ def get_config(module, flags=[]):
         _DEVICE_CONFIGS[cmd] = cfg
         return cfg
 
-def to_commands(commands):
-    transform = ComplexList(dict(
-        command=dict(key=True),
-        prompt=dict(),
-        response=dict()
-    ))
+def to_commands(module, commands):
+    spec = {
+        'command': dict(key=True),
+        'prompt': dict(),
+        'response': dict()
+    }
+    transform = ComplexList(spec, module)
     return transform(commands)
 
 
 def run_commands(module, commands, check_rc=True):
     responses = list()
-    commands = to_commands(to_list(commands))
+    commands = to_commands(module, to_list(commands))
     for cmd in commands:
         cmd = module.jsonify(cmd)
         rc, out, err = exec_command(module, cmd)
diff --git a/lib/ansible/plugins/action/ios.py b/lib/ansible/plugins/action/ios.py
--- a/lib/ansible/plugins/action/ios.py
+++ b/lib/ansible/plugins/action/ios.py
@@ -53,10 +53,11 @@ def run(self, tmp=None, task_vars=None):
         pc.become = provider['authorize'] or False
         pc.become_pass = provider['auth_pass']
 
+        connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin)
+
         socket_path = self._get_socket_path(pc)
         if not os.path.exists(socket_path):
             # start the connection if it isn't started
-            connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin)
             connection.exec_command('EXEC: show version')
 
         task_vars['ansible_socket'] = socket_path
@@ -66,7 +67,14 @@ def run(self, tmp=None, task_vars=None):
             self._play_context.become_method = None
 
 
-        return super(ActionModule, self).run(tmp, task_vars)
+        results = super(ActionModule, self).run(tmp, task_vars)
+
+        # need to make sure to leave config mode if the module didn't clean up
+        rc, out, err = connection.exec_command('EXEC: prompt()')
+        if str(out).strip().endswith(')#'):
+            connection.exec_command('EXEC: exit')
+
+        return results
 
     def _get_socket_path(self, play_context):
         ssh = connection_loader.get('ssh', class_only=True)


Test patch: