Problem with running playbook on hosts with different Python versions

I wrote a simple playbook to collect information regarding OS version for all hosts in inventory:

`

  • name: “Get OS versions Ansible playbook”
    hosts: all
    gather_facts: no

tasks:

  • name: “Fetch OS details”
    shell: “cat /etc/redhat-release”
    register: result

  • name: “Print OS details”
    debug: msg=“redhat-release = ‘{{ result.stdout }}’”
    `

$ ansible-playbook -i inventory get-os-varsions.yml

Unfortunately on few hosts I received bizarre error:

fatal: [host1]: FAILED! => { "changed": false, "failed": true, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\r\n File \"/tmp/ansible_uSOBbv/ansible_module_command.py\", line 98, in ?\r\n from ansible.module_utils.basic import AnsibleModule\r\n File \"/tmp/ansible_uSOBbv/ansible_modlib.zip/ansible/module_utils/basic.py\", line 93, in ?\r\nImportError: cannot import name Mapping\r\n", "msg": "MODULE FAILURE", "rc": 1 }

therefore I enabled keeping remote files:

$ export ANSIBLE_KEEP_REMOTE_FILES=1

and executed playbook just for failed host:

$ ansible-playbook -i inventory get-os-varsions.yml --limit host1 -vvvv

Using module_utils file /usr/lib/python2.7/site-packages/ansible/module_utils/basic.py Using module_utils file /usr/lib/python2.7/site-packages/ansible/module_utils/six/__init__.py Using module_utils file /usr/lib/python2.7/site-packages/ansible/module_utils/_text.py Using module_utils file /usr/lib/python2.7/site-packages/ansible/module_utils/pycompat24.py Using module_utils file /usr/lib/python2.7/site-packages/ansible/module_utils/six/_six.py Using module file /usr/lib/python2.7/site-packages/ansible/modules/commands/command.py <plabb59.sgdcelab.sabre.com> ESTABLISH CONNECTION FOR USER: funcusr on PORT 22 TO plabb59.sgdcelab.sabre.com <plabb59.sgdcelab.sabre.com> EXEC /bin/sh -c '( umask 77 && mkdir -p " echo /tmp/ansible-tmp-1500044507.29-45810672852156 " && echo ansible-tmp-1500044507.29-45810672852156=" echo /tmp/ansible-tmp-1500044507.29-45810672852156 " ) && sleep 0' <plabb59.sgdcelab.sabre.com> PUT /tmp/tmpv_ifKv TO /tmp/ansible-tmp-1500044507.29-45810672852156/command.py <plabb59.sgdcelab.sabre.com> EXEC /bin/sh -c 'chmod u+x /tmp/ansible-tmp-1500044507.29-45810672852156/ /tmp/ansible-tmp-1500044507.29-45810672852156/command.py && sleep 0' <plabb59.sgdcelab.sabre.com> EXEC /bin/sh -c '/usr/bin/python /tmp/ansible-tmp-1500044507.29-45810672852156/command.py && sleep 0'

ssh into failed host, went to /tmp/ansible-tmp-… and executed command by hand:

$ /usr/bin/python /tmp/ansible-tmp-1500044507.29-45810672852156/command.py

result was the same:

Traceback (most recent call last): File "/tmp/ansible_TLPERw/ansible_module_command.py", line 98, in ? from ansible.module_utils.basic import AnsibleModule File "/tmp/ansible_TLPERw/ansible_modlib.zip/ansible/module_utils/basic.py", line 93, in ? ImportError: cannot import name Mapping

I started to think was is going on? I noticed that on controller host python2.7 is used, but on remote host there is:

$ ll /usr/bin/python*
/usr/bin/python (python2.4)
/usr/bin/python2 → python
/usr/bin/python2.4
/usr/bin/python26
/usr/bin/python2.6
/usr/bin/python2.6-config

therefore I altered a little bit an environment:

cd /usr/bin/

mv python python.bak

ln -s python2.6 python

Then I returned to /tmp/ansible-tmp-… and executed

$ /usr/bin/python /tmp/ansible-tmp-1500044507.29-45810672852156/command.py

result was successful:

{"changed": true, "end": "2017-07-14 10:19:09.499954", "stdout": "Red Hat Enterprise Linux Server release 5.11 (Tikanga)", "cmd": "cat /etc/redhat-release", "rc": 0, "start": "2017-07-14 10:19:09.492287", "stderr": "", "delta": "0:00:00.007667", "invocation": {"module_args": {"warn": true, "executable": null, "_uses_shell": true, "_raw_params": "cat /etc/redhat-release", "removes": null, "creates": null, "chdir": null}}}

Just for curiosity I opened command.py and I noticed huge chunk of base64 encoded something called ZIPDATA.
Since I could not find AnsibleModule inside command.py (it was in traceback), therefore it must be hidden
inside this big chunk of something. I guess the clue of problem is that pythion2.7 prepares this chunk of something
but python2.4 cannot understand this something and bails out with nasty traceback which is unreadable to regular Ansible user.

Does anyone know how to avoid such a problem on environments which different OS versions (and thus Python interpreters)?

This is what ansible_python_interpreter is for
http://docs.ansible.com/ansible/intro_inventory.html (it is in bottom
3rd of the page).

Python 2.4 is probably not supported.

(..)
Managed Node Requirement:

You also need Python 2.6 or later.
(..)

Up to Ansible 2.3, python 2.4 is supported on the targets, but not all
modules, specially those that depend on 3rd party libs are guaranteed
to work.

Starting in Ansible 2.4 we no longer support pytnon 2.4/2.5 on target machines.

The controller has always required 2.6 and will continue to support that.