Repository: ansible/ansible

Issue:
python3 - lxc_container broken, files are opened with 'b' but treated as strings
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME

cloud/lxc/lxc_container.py


##### ANSIBLE VERSION
git master (2.5.0)
##### CONFIGURATION
ANSIBLE_NOCOWS(env: ANSIBLE_NOCOWS) = True
PERSISTENT_CONNECT_TIMEOUT(/etc/ansible/ansible.cfg) = 30

##### OS / ENVIRONMENT

Control Debian/Sid
Target: Ubuntu/17.04

##### SUMMARY

Python 3 since file is opened as binary but later used as text
```python3
  File "/tmp/ansible_kck92z7m/ansible_module_lxc_container.py", line 743, in _config
    if keyre.match(option_line):
TypeError: cannot use a string pattern on a bytes-like object
```

##### STEPS TO REPRODUCE
try to create a new container

```yaml
 lxc_container:
    name py3test
    template: ubuntu
    state: started
```

##### EXPECTED RESULTS

should create the container if it does not exist

##### ACTUAL RESULTS
```
failed: [host] (item={'template': 'ubuntu', 'name': 'py3test', 'state': 'started', 'template_options': '--release zesty', 'container_config': []}) => {"changed": false, "failed": true, "item": {"container_config": ["lxc.start.auto=0"], "name": "py3test", "state": "started", "template": "ubuntu", "template_options": "--release zesty"}, "module_stderr": "Shared connection to dd-c closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/tmp/ansible_4rnj_ldq/ansible_module_lxc_container.py\", line 1771, in <module>\r\n    main()\r\n  File \"/tmp/ansible_4rnj_ldq/ansible_module_lxc_container.py\", line 1767, in main\r\n    lxc_manage.run()\r\n  File \"/tmp/ansible_4rnj_ldq/ansible_module_lxc_container.py\", line 1647, in run\r\n    action()\r\n  File \"/tmp/ansible_4rnj_ldq/ansible_module_lxc_container.py\", line 1182, in _started\r\n    self._config()\r\n  File \"/tmp/ansible_4rnj_ldq/ansible_module_lxc_container.py\", line 743, in _config\r\n    if keyre.match(option_line):\r\nTypeError: cannot use a string pattern on a bytes-like object\r\n", "msg": "MODULE FAILURE", "rc": 0}
```



Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/cloud/lxc/lxc_container.py b/lib/ansible/modules/cloud/lxc/lxc_container.py
--- a/lib/ansible/modules/cloud/lxc/lxc_container.py
+++ b/lib/ansible/modules/cloud/lxc/lxc_container.py
@@ -438,6 +438,7 @@
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE, BOOLEANS_TRUE
 from ansible.module_utils.six.moves import xrange
+from ansible.module_utils._text import to_text, to_bytes
 
 
 # LXC_COMPRESSION_MAP is a map of available compression types when creating
@@ -572,7 +573,7 @@ def create_script(command):
     (fd, script_file) = tempfile.mkstemp(prefix='lxc-attach-script')
     f = os.fdopen(fd, 'wb')
     try:
-        f.write(ATTACH_TEMPLATE % {'container_command': command})
+        f.write(to_bytes(ATTACH_TEMPLATE % {'container_command': command}, errors='surrogate_or_strict'))
         f.flush()
     finally:
         f.close()
@@ -722,7 +723,7 @@ def _config(self):
 
         container_config_file = self.container.config_file_name
         with open(container_config_file, 'rb') as f:
-            container_config = f.readlines()
+            container_config = to_text(f.read(), errors='surrogate_or_strict').splitlines()
 
         # Note used ast literal_eval because AnsibleModule does not provide for
         # adequate dictionary parsing.
@@ -763,7 +764,7 @@ def _config(self):
                 self.container.stop()
 
             with open(container_config_file, 'wb') as f:
-                f.writelines(container_config)
+                f.writelines([to_bytes(line, errors='surrogate_or_strict') for line in container_config])
 
             self.state_change = True
             if container_state == 'running':


Test patch: