Ansible playbook run fails due to Python version problem: How to force a Python version on the client

Written by - 5 comments

Published on - last updated on March 20th 2023 - Listed in Ansible Linux Python Personal

When a (pretty basic) Ansible playbook was run against a SLES 15 SP1 client, the playbook already stopped after the first few tasks with a Python related error message:

TASK [MONI - Install Nagios packages (SLES10/11)] ******************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ImportError: No module named xml
failed: [sles15.local] (item=[u'nrpe', u'nagios-plugins-all', u'ksh', u'sysstat']) => {"ansible_loop_var": "item", "changed": false, "item": ["nrpe", "nagios-plugins-all", "ksh", "sysstat"], "module_stderr": "Shared connection to closed.\r\n", "module_stdout": "Traceback (most recent call last):\r\n  File \"/home/ansible/.ansible/tmp/ansible-tmp-1599726118.74-10511-262880492763867/\", line 102, in \r\n    _ansiballz_main()\r\n  File \"/home/ansible/.ansible/tmp/ansible-tmp-1599726118.74-10511-262880492763867/\", line 94, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/home/ansible/.ansible/tmp/ansible-tmp-1599726118.74-10511-262880492763867/\", line 40, in invoke_module\r\n    runpy.run_module(mod_name='ansible.modules.packaging.os.zypper', init_globals=None, run_name='__main__', alter_sys=True)\r\n  File \"/usr/lib64/python2.7/\", line 188, in run_module\r\n    fname, loader, pkg_name)\r\n  File \"/usr/lib64/python2.7/\", line 82, in _run_module_code\r\n    mod_name, mod_fname, mod_loader, pkg_name)\r\n  File \"/usr/lib64/python2.7/\", line 72, in _run_code\r\n    exec code in run_globals\r\n  File \"/tmp/ansible_zypper_payload_cpZrw_/\", line 195, in \r\nImportError: No module named xml\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

PLAY RECAP ******************************************************************************************
sles15.local    : ok=1    changed=0    unreachable=0    failed=1    skipped=7    rescued=0    ignored=0

On the SLES 15 machine, this can quickly be verified by manually trying to import the xml module into Python:

sles15:~ # python
Python 2.7.17 (default, Nov 04 2019, 16:08:54) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import xml
Traceback (most recent call last):
  File "", line 1, in
ImportError: No module named xml

Obviously an older Python 2.7 is used here as default Python version - even though Python 3.6 is installed at the same time.

Install the missing Python library using zypper

The obvious solution is to simply install the missing module/library from the package manager (zypper). However when looking for the relevant package (libxml2-python), only the source package python-libxml2-python was available for Python 2 - while the "normal" package python3-libxml2-python was available for Python 3:

sles15:~ # zypper se libxml2-python
Refreshing service 'Basesystem_Module_15_SP1_x86_64'.
Refreshing service 'SUSE_Linux_Enterprise_Server_15_SP1_x86_64'.
Refreshing service 'SUSE_Package_Hub_15_SP1_x86_64'.
Refreshing service 'Server_Applications_Module_15_SP1_x86_64'.
Loading repository data...
Reading installed packages...

S  | Name                   | Summary                     | Type
   | python-libxml2-python  | Python Bindings for libxml2 | srcpackage
   | python3-libxml2-python | Python Bindings for libxml2 | package

Installing the python3-libxml2-python package was easy:

sles15:~ # zypper in python3-libxml2-python
Refreshing service 'Basesystem_Module_15_SP1_x86_64'.
Refreshing service 'SUSE_Linux_Enterprise_Server_15_SP1_x86_64'.
Refreshing service 'SUSE_Package_Hub_15_SP1_x86_64'.
Refreshing service 'Server_Applications_Module_15_SP1_x86_64'.
Loading repository data...
Reading installed packages...
Resolving package dependencies...

The following NEW package is going to be installed:

1 new package to install.
Overall download size: 221.0 KiB. Already cached: 0 B. After the operation, additional 1.6 MiB will be used.
Continue? [y/n/v/...? shows all options] (y): y
Retrieving package python3-libxml2-python-2.9.7-3.22.1.x86_64                                          (1/1), 221.0 KiB (  1.6 MiB unpacked)
Retrieving: python3-libxml2-python-2.9.7-3.22.1.x86_64.rpm ........................[done]
Checking for file conflicts: ......................................................[done]
(1/1) Installing: python3-libxml2-python-2.9.7-3.22.1.x86_64 ......................[done]

And importing the xml module in Python 3 was working, too:

sles15:~ # python3
Python 3.6.10 (default, Dec 19 2019, 15:48:40) [GCC] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import xml
>>> exit()

However the Ansible playbook run still failed due to the same error message. Obviously Python 2 is used by default, as python on this SLES 15 machine points to Python 2:

sles15:~ # ll /usr/bin/python
lrwxrwxrwx 1 root root 9 May  4 18:01 /usr/bin/python -> python2.7

And the missing libxml2 library could not be installed for Python 2 on this SLES 15 machine...

A possible quick and dirty fix would be to change the symlink to point to /usr/bin/python3 - but maybe other scripts are running on this SLES 15 machine which rely on Python 2 and this could potentially break a lot of things. But there's a better way!

Forcing the Python version in Ansible inventory

Ansible can be told which Python version should be used on the client. By using the setting ansible_python_interpreter, a specific Python path/version can be defined on a per client basis (hint found in Ansible issue 59686):

ansible@ansibleserver:~$ grep -h sles15 inventory/*
sles15.local ansible_ssh_host= ansible_python_interpreter=/usr/bin/python3

The playbook now runs with Python 3 on the SLES 15 machine and finally finished with success:

PLAY RECAP *****************************************************************************************
sles15.local    : ok=14   changed=6    unreachable=0    failed=0    skipped=24   rescued=0    ignored=0

Using update-alternatives to point python to python3

As suggested in the comments, another possibility is to use update-alternatives to point /usr/bin/python to /usr/bin/python3.

In other news: This is blog post 1000!!!

Wow. The counter increased to a four digit number now! This is my 1000th blog post! It took me more than 12 years to achieve this number. The first blog post (Welcome to was published on May 26th 2008. A long time ago. Back then virtual machines was the current and still upcoming hype. Nobody talked of containers let alone Kubernetes back then.

Blog post 1000! This is cool!

But not only technical stuff surrounding me changed - in the meantime I married and became a father or two wonderful (most of the times anyway) kids. Besides this I co-founded Infiniroot where customer tasks and project have a priority. This clearly takes a lot of my time.

Will there be a 2000th entry in the future? Only time will tell. But it will clearly be difficult. But as long as there are still challenges left to resolve (oh yes, there will), new blog articles will continue to roll in.

Add a comment

Show form to leave a comment

Comments (newest first)

Glowsome from Netherlands wrote on Jun 26th, 2021:

You can also use alternatives to point to the correct python3 version after installing the suggested RPM (python3-libxml2-python)

execute : update-alternatives --install /usr/bin/python python /usr/bin/python3 300
output: update-alternatives: using /usr/bin/python3 to provide /usr/bin/python (python) in auto mode

check : update-alternatives --display python
output: python - auto mode
link best version is /usr/bin/python3
link currently points to /usr/bin/python3
link python is /usr/bin/python
/usr/bin/python3 - priority 300

Double-check: ll /usr/bin/python
output: lrwxrwxrwx 1 root root 24 Jun 26 00:35 /usr/bin/python -> /etc/alternatives/python

Rafael from São Paulo - Brazil wrote on Sep 11th, 2020:

By the way, man. Congratz on the post 1000.

Rafael from wrote on Sep 10th, 2020:

Hi Claudio. It was the same error that you have shared. For me was necessary to install the python-xml package too. Now it is working! Thank you, man.

ck from Switzerland wrote on Sep 10th, 2020:

Hi Rafael. So what's the exact error? Also ImportError on xml? Are you sure the Ansible client is running on Python 3? Is this a SLES15, too?

Rafael from wrote on Sep 10th, 2020:

Mna, same error here, and to install the xml didn't work.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Office   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder