Repository: ansible/ansible-modules-core

Issue:
before parameter crashes ios_config
##### ISSUE TYPE

<!--- Pick one below and delete the rest: -->
- Bug Report
##### COMPONENT NAME

ios_config
##### ANSIBLE VERSION

<!--- Paste verbatim output from “ansible --version” between quotes below -->

```
ansible-playbook 2.2.0 (devel d5aecfdd14) last updated 2016/09/05 19:01:49 (GMT +000)
  lib/ansible/modules/core: (detached HEAD 982c4557d2) last updated 2016/09/05 19:01:58 (GMT +000)
  lib/ansible/modules/extras: (detached HEAD 06bd2a5ce2) last updated 2016/09/05 19:02:05 (GMT +000)
  config file = /vagrant/Ansible/Networking/ansible.cfg
  configured module search path = Default w/o overrides
```
##### CONFIGURATION

```
[defaults]
inventory=./hosts
gathering=explicit
transport=local
```
##### OS / ENVIRONMENT

Ansible: Ubuntu
Target: Cisco IOSv
##### SUMMARY

Simple ios_config tasks work correctly, before parameter crashes ios_config.py
##### STEPS TO REPRODUCE

```

---
- hosts: ios
  tasks:
  - name: "Configure ACL on Cisco IOS"
    ios_config:
      provider: "{{cli}}"
      parents:
        - "ip access-list extended AllowedTraffic"
      lines:
        - "permit tcp any eq www any"
        - "permit tcp any any eq www"
        - "deny   tcp any any log"
        - "deny   ip any any log"
      match: exact
      before:
        - "no ip access-list extended AllowedTraffic"
```
##### ACTUAL RESULTS

<!--- Paste verbatim command output between quotes below -->

```
ansible-playbook -vvv config-acl-add-before.yml
Using /vagrant/Ansible/Networking/ansible.cfg as config file

PLAYBOOK: config-acl-add-before.yml ********************************************
1 plays in config-acl-add-before.yml

PLAY [ios] *********************************************************************

TASK [Configure ACL on Cisco IOS] **********************************************
task path: /vagrant/Ansible/Networking/config-acl-add-before.yml:11
Using module file /home/vagrant/ansible/lib/ansible/modules/core/network/ios/ios_config.py
<r1.lab.local> ESTABLISH LOCAL CONNECTION FOR USER: vagrant
<r1.lab.local> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1473102645.24-16932177008970 `" && echo ansible-tmp-1473102645.24-16932177008970="` echo $HOME/.ansible/tmp/ansible-tmp-1473102645.24-16932177008970 `" ) && sleep 0'
<r1.lab.local> PUT /tmp/tmpHUzWjZ TO /home/vagrant/.ansible/tmp/ansible-tmp-1473102645.24-16932177008970/ios_config.py
<r1.lab.local> EXEC /bin/sh -c 'chmod u+x /home/vagrant/.ansible/tmp/ansible-tmp-1473102645.24-16932177008970/ /home/vagrant/.ansible/tmp/ansible-tmp-1473102645.24-16932177008970/ios_config.py && sleep 0'
<r1.lab.local> EXEC /bin/sh -c '/usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1473102645.24-16932177008970/ios_config.py; rm -rf "/home/vagrant/.ansible/tmp/ansible-tmp-1473102645.24-16932177008970/" > /dev/null 2>&1 && sleep 0'
An exception occurred during task execution. The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_vt3BEz/ansible_module_ios_config.py", line 409, in <module>
    main()
  File "/tmp/ansible_vt3BEz/ansible_module_ios_config.py", line 399, in main
    invoke(state, module, result)
  File "/tmp/ansible_vt3BEz/ansible_module_ios_config.py", line 253, in invoke
    return func(*args, **kwargs)
  File "/tmp/ansible_vt3BEz/ansible_module_ios_config.py", line 324, in present
    commands[:0] = module.params['before']
TypeError: 'str' object does not support item assignment

fatal: [r1.lab.local]: FAILED! => {
    "changed": false,
    "failed": true,
    "invocation": {
        "module_args": {
            "before": [
                "no ip access-list extended AllowedTraffic"
            ],
            "lines": [
                "permit tcp any eq www any",
                "permit tcp any any eq www",
                "deny   tcp any any log",
                "deny   ip any any log"
            ],
            "match": "exact",
            "parents": [
                "ip access-list extended AllowedTraffic"
            ],
            "provider": {
                "host": "r1.lab.local",
                "password": "cisco",
                "transport": "cli",
                "username": "cisco"
            }
        },
        "module_name": "ios_config"
    },
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_vt3BEz/ansible_module_ios_config.py\", line 409, in <module>\n    main()\n  File \"/tmp/ansible_vt3BEz/ansible_module_ios_config.py\", line 399, in main\n    invoke(state, module, result)\n  File \"/tmp/ansible_vt3BEz/ansible_module_ios_config.py\", line 253, in invoke\n    return func(*args, **kwargs)\n  File \"/tmp/ansible_vt3BEz/ansible_module_ios_config.py\", line 324, in present\n    commands[:0] = module.params['before']\nTypeError: 'str' object does not support item assignment\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE"
}
        to retry, use: --limit @config-acl-add-before.retry

PLAY RECAP *********************************************************************
r1.lab.local               : ok=0    changed=0    unreachable=0    failed=1
```



Discussion:
<!-- @ipspace: Thanks for filing this bug report! -->

@privateip, @gundalow, ping. This issue is waiting on your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_bug_new --->

@ipspace Thanks for the bug report, and steps to reproduce we will look into this.

Blocking against `2.2.0` as this needs fixing


Resolving patch:
diff --git a/network/ios/ios_config.py b/network/ios/ios_config.py
--- a/network/ios/ios_config.py
+++ b/network/ios/ios_config.py
@@ -288,7 +288,7 @@ def load_config(module, commands, result):
     if not module.check_mode and module.params['update'] != 'check':
         module.config(commands)
     result['changed'] = module.params['update'] != 'check'
-    result['updates'] = commands.split('\n')
+    result['updates'] = commands
 
 def run(module, result):
     match = module.params['match']
@@ -304,7 +304,7 @@ def run(module, result):
         configobjs = candidate.items
 
     if configobjs:
-        commands = dumps(configobjs, 'commands')
+        commands = dumps(configobjs, 'commands').split('\n')
 
         if module.params['before']:
             commands[:0] = module.params['before']
diff --git a/network/iosxr/iosxr_config.py b/network/iosxr/iosxr_config.py
--- a/network/iosxr/iosxr_config.py
+++ b/network/iosxr/iosxr_config.py
@@ -264,7 +264,7 @@ def run(module, result):
         configobjs = candidate.items
 
     if configobjs:
-        commands = dumps(configobjs, 'commands')
+        commands = dumps(configobjs, 'commands').split('\n')
 
         if module.params['before']:
             commands[:0] = module.params['before']
@@ -272,7 +272,7 @@ def run(module, result):
         if module.params['after']:
             commands.extend(module.params['after'])
 
-        result['updates'] = commands.split('\n')
+        result['updates'] = commands
 
         if update != 'check':
             load_config(module, commands, result)
diff --git a/network/openswitch/ops_config.py b/network/openswitch/ops_config.py
--- a/network/openswitch/ops_config.py
+++ b/network/openswitch/ops_config.py
@@ -237,7 +237,7 @@ def load_config(module, commands, result):
     if not module.check_mode and module.params['update'] != 'check':
         module.config(commands)
     result['changed'] = module.params['update'] != 'check'
-    result['updates'] = commands.split('\n')
+    result['updates'] = commands
 
 def run(module, result):
     match = module.params['match']
@@ -253,7 +253,7 @@ def run(module, result):
         configobjs = candidate.items
 
     if configobjs:
-        commands = dumps(configobjs, 'commands')
+        commands = dumps(configobjs, 'commands').split('\n')
 
         if module.params['before']:
             commands[:0] = module.params['before']


Test patch: