Repository: ansible/ansible

Issue:
locale_gen fails with python3
Ansible 2.2.0.0 on osx Sierra (brew install)
pipelining = True

passed option -e 'ansible_python_interpreter=/usr/bin/python3'

playbook runs ok with python2 (installed in a pre-task, as the target is Ubuntu 16.04)

`fatal: [192.168.239.194]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Shared connection to 192.168.239.194 closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n File \"/tmp/ansible_lhviwmqc/ansible_module_locale_gen.py\", line 239, in <module>\r\n main()\r\n File \"/tmp/ansible_lhviwmqc/ansible_module_locale_gen.py\", line 217, in main\r\n if is_present(name):\r\n File \"/tmp/ansible_lhviwmqc/ansible_module_locale_gen.py\", line 96, in is_present\r\n return any(fix_case(name) == fix_case(line) for line in output.splitlines())\r\n File \"/tmp/ansible_lhviwmqc/ansible_module_locale_gen.py\", line 96, in <genexpr>\r\n return any(fix_case(name) == fix_case(line) for line in output.splitlines())\r\n File \"/tmp/ansible_lhviwmqc/ansible_module_locale_gen.py\", line 101, in fix_case\r\n for s, r in LOCALE_NORMALIZATION.iteritems():\r\nAttributeError: 'dict' object has no attribute 'iteritems'\r\n", "msg": "MODULE FAILURE"}`



Discussion:
I believe this is fixed in the devel branch. Can you please try with the devel version?
sadly no, I get a different error
This time I'm using Ansible 2.3 (devel) from a fresly created docker (alpine linux)

`fatal: [192.168.239.194]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Shared connection to 192.168.239.194 closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/tmp/ansible_hjvbi4qf/ansible_module_locale_gen.py\", line 246, in <module>\r\n    main()\r\n  File \"/tmp/ansible_hjvbi4qf/ansible_module_locale_gen.py\", line 223, in main\r\n    if is_present(name):\r\n  File \"/tmp/ansible_hjvbi4qf/ansible_module_locale_gen.py\", line 102, in is_present\r\n    return any(fix_case(name) == fix_case(line) for line in output.splitlines())\r\n  File \"/tmp/ansible_hjvbi4qf/ansible_module_locale_gen.py\", line 102, in <genexpr>\r\n    return any(fix_case(name) == fix_case(line) for line in output.splitlines())\r\n  File \"/tmp/ansible_hjvbi4qf/ansible_module_locale_gen.py\", line 108, in fix_case\r\n    name = name.replace(s, r)\r\nTypeError: a bytes-like object is required, not 'str'\r\n", "msg": "MODULE FAILURE"}`
This issue was addressed by https://github.com/ansible/ansible-modules-extras/pull/3459, but I'm still having issues too.

__example setup__,

ansible version,
```sh
ansible --version
ansible 2.3.0
  config file = /path/to/my_ansible_playbooks_repo/ansible.cfg
  configured module search path = Default w/o overrides
```

hosts file,
```yml
[myhosts:children]
production

[production]
1.1.1.1

[production:vars]
ansible_python_interpreter=/usr/bin/python3
env=production
rails_env=production
```

playbook,
```yml
---
- name: server configure
  hosts: myhosts
  user: ubuntu
  gather_facts: false
  tasks:
    - name: locale en_US.UTF-8
      become_user: root
      become: yes
      locale_gen: name=en_US.UTF-8 state=present
```

playbook kicked off via,
```sh
ansible-playbook    -i hosts/hostfile   --vault-password-file ~/.ansiblevault_password.txt  server.yml --verbose
```

resulting output,
```sh
TASK [locale en_US.UTF-8] *****************************************************************
fatal: [1.1.1.1]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Connection to 1.1.1.1 closed.\r\n", "module_stdout": "\r\nTraceback (most recent call last):\r\n  File \"/tmp/ansible_67z5z0ua/ansible_module_locale_gen.py\", line 246, in <module>\r\n    main()\r\n  File \"/tmp/ansible_67z5z0ua/ansible_module_locale_gen.py\", line 223, in main\r\n    if is_present(name):\r\n  File \"/tmp/ansible_67z5z0ua/ansible_module_locale_gen.py\", line 102, in is_present\r\n    return any(fix_case(name) == fix_case(line) for line in output.splitlines())\r\n  File \"/tmp/ansible_67z5z0ua/ansible_module_locale_gen.py\", line 102, in <genexpr>\r\n    return any(fix_case(name) == fix_case(line) for line in output.splitlines())\r\n  File \"/tmp/ansible_67z5z0ua/ansible_module_locale_gen.py\", line 108, in fix_case\r\n    name = name.replace(s, r)\r\nTypeError: a bytes-like object is required, not 'str'\r\n", "msg": "MODULE FAILURE", "rc": 0}
	to retry, use: --limit @/my/path/server_playbook.retry
```

Resolving patch:
diff --git a/lib/ansible/modules/system/locale_gen.py b/lib/ansible/modules/system/locale_gen.py
--- a/lib/ansible/modules/system/locale_gen.py
+++ b/lib/ansible/modules/system/locale_gen.py
@@ -55,8 +55,9 @@
 from subprocess import Popen, PIPE, call
 import re
 
-from ansible.module_utils.basic import *
+from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.pycompat24 import get_exception
+from ansible.module_utils._text import to_native
 
 LOCALE_NORMALIZATION = {
     ".utf8": ".UTF-8",
@@ -99,6 +100,7 @@ def is_available(name, ubuntuMode):
 def is_present(name):
     """Checks if the given locale is currently installed."""
     output = Popen(["locale", "-a"], stdout=PIPE).communicate()[0]
+    output = to_native(output)
     return any(fix_case(name) == fix_case(line) for line in output.splitlines())
 
 def fix_case(name):


Test patch:
diff --git a/test/integration/destructive.yml b/test/integration/destructive.yml
--- a/test/integration/destructive.yml
+++ b/test/integration/destructive.yml
@@ -22,3 +22,4 @@
     - { role: apache2_module, tags: test_apache2_module }
     # This removes ~/.ssh/known_hosts and /etc/ssh/known_hosts
     - { role: git, tags: test_git }
+    - { role: locale_gen, tags: test_locale_gen }
diff --git a/test/integration/targets/locale_gen/aliases b/test/integration/targets/locale_gen/aliases
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/locale_gen/aliases
@@ -0,0 +1,3 @@
+destructive
+needs/root
+posix/ci/group3
diff --git a/test/integration/targets/locale_gen/meta/main.yml b/test/integration/targets/locale_gen/meta/main.yml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/locale_gen/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+  - prepare_tests
diff --git a/test/integration/targets/locale_gen/tasks/locale_gen.yml b/test/integration/targets/locale_gen/tasks/locale_gen.yml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/locale_gen/tasks/locale_gen.yml
@@ -0,0 +1,94 @@
+- name: Is the locale we're going to test against installed?
+  shell: locale -a | grep pt_BR
+  register: initial_state
+  ignore_errors: True
+
+- name: Make sure the locale is not installed
+  locale_gen:
+    name: pt_BR
+    state: absent
+
+- name: Is the locale present?
+  shell: locale -a | grep pt_BR
+  register: cleaned
+  ignore_errors: True
+
+- name: Make sure the locale is not present
+  assert:
+    that:
+      - "cleaned.rc == 1"
+
+- name: Install the locale
+  locale_gen:
+    name: pt_BR
+    state: present
+  register: output
+
+- name: Is the locale present?
+  shell: locale -a | grep pt_BR
+  register: post_check_output
+  ignore_errors: True
+
+- name: Make sure the locale is present and we say we installed it
+  assert:
+    that:
+      - "post_check_output.rc == 0"
+      - "output.changed"
+
+- name: Install the locale a second time
+  locale_gen:
+    name: pt_BR
+    state: present
+  register: output
+
+- name: Is the locale present?
+  shell: locale -a | grep pt_BR
+  register: post_check_output
+  ignore_errors: True
+
+- name: Make sure the locale is present and we reported no change
+  assert:
+    that:
+      - "post_check_output.rc == 0"
+      - "not output.changed"
+
+- name: Remove the locale
+  locale_gen:
+    name: pt_BR
+    state: absent
+  register: output
+
+- name: Is the locale present?
+  shell: locale -a | grep pt_BR
+  register: post_check_output
+  ignore_errors: True
+
+- name: Make sure the locale is absent and we reported a change
+  assert:
+    that:
+      - "post_check_output.rc == 1"
+      - "output.changed"
+
+- name: Remove the locale a second time
+  locale_gen:
+    name: pt_BR
+    state: absent
+  register: output
+
+- name: Is the locale present?
+  shell: locale -a | grep pt_BR
+  register: post_check_output
+  ignore_errors: True
+
+- name: Make sure the locale is absent and we reported no change
+  assert:
+    that:
+      - "post_check_output.rc == 1"
+      - "not output.changed"
+
+# Cleanup
+- name: Reinstall the locale we tested against if it was initially installed
+  locale_gen:
+    name: pt_BR
+    state: present
+  when: initial_state.rc == 0
diff --git a/test/integration/targets/locale_gen/tasks/main.yml b/test/integration/targets/locale_gen/tasks/main.yml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/locale_gen/tasks/main.yml
@@ -0,0 +1,19 @@
+# (c) 2014, James Tanner <tanner.jc@gmail.com>
+
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
+
+- include: 'locale_gen.yml'
+  when: ansible_distribution in ('Ubuntu', 'Debian')