I have an Ansible playbook using a role that should do some basic Linux setup tasks.
- OS: Oracle Linux 9.5 (so, basically RHEL)
- Ansible 2.14
- ansible.posix 1.5.4 (hard dependency)
- Python 3.9
- Ansible controller and target are the same; connection is local.
All ran fine until I started to use a Python virtual environment, with the goal to encapsulate all dependencies in it, including Ansible-Galaxy Collections. I set ANSIBLE_COLLECTIONS_PATH=/home/ansible/ansible-oracle/.venv/lib/collections
to point to the venv.
The moment I removed all those Collections from /usr/share
, one single part of this playbook ran into the following error:
fatal: [host01]: FAILED! => changed=false
msg: Failed to import the required Python library (firewall) on host01's Python /home/ansible/ansible-oracle/.venv/bin/python3.9. Please read the module documentation and install it in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter. Version 0.2.11 or newer required (0.3.9 or newer for offline operations)
The strange thing is, there are other Galaxy Collections used in the playbook (e.g., community.general) which do just fine. The only task that breaks is the one with ansible.posix.firewalld
.
The playbook is as simple as this:
- hosts: "{{ hostgroup | default('all') }}"
become: yes
roles:
- basic-linux
And here's the task causing the error:
- name: allow connections on oracle database default port
ansible.posix.firewalld:
port: 1521/tcp
permanent: true
immediate: true
state: enabled
Check for a possible version mismatch:
$ ansible --version | grep "python version"
python version = 3.9.19 (main, Aug 23 2024, 00:00:00) [GCC 11.5.0 20240719 (Red Hat 11.5.0-2.0.1)] (/usr/bin/python3)
$ python --version
Python 3.9.19
$ /usr/bin/python --version
Python 3.9.19
When I install the Collections globally (read: /usr/share/...), the task runs just fine. But I need a self-contained environment that doesn't break when someone upgrades something global.
While researching I stumbled upon this suggestion and modified the task to look like this (last 2 lines added):
- name: allow connections on oracle database default port
ansible.posix.firewalld:
port: 1521/tcp
permanent: true
immediate: true
state: enabled
vars:
ansible_python_interpreter: /usr/bin/python
Then it runs, but why?
As the Ansible host and the target host are one and the same, the Python versions used are also identical. This even applies to the venv for now, because Python in the venv was installed from the same source (the OS install ISO image).
Also, when the Collections are installed in /usr/share, they would still use the same Python version, or not? And why is only ansible.posix.firewalld
affected but not community.general.*
?
Before you point me to the loads of answers to similar questions: I've reviewed what I could find, but there never was a fitting explanation for my particular issue. That is: the moment ansible_python_interpreter: /usr/bin/python
is set instead of the default python3
, it works.
Edit: My best guess is that this has to do with the host connection being defined as 'local':
---
all:
children:
test:
hosts:
host01:
ansible_host: local
ansible_connection: local
When I change it to an SSH connection it runs without said error as well.
Could it be that on "local" connections, the venv settings are used while running in the target context? That would be sub-optimal.
Can anyone confirm my guess? If so, can the local connect be changed to behave like the ssh connect (e.g., use python from $PATH)?
I have an Ansible playbook using a role that should do some basic Linux setup tasks.
- OS: Oracle Linux 9.5 (so, basically RHEL)
- Ansible 2.14
- ansible.posix 1.5.4 (hard dependency)
- Python 3.9
- Ansible controller and target are the same; connection is local.
All ran fine until I started to use a Python virtual environment, with the goal to encapsulate all dependencies in it, including Ansible-Galaxy Collections. I set ANSIBLE_COLLECTIONS_PATH=/home/ansible/ansible-oracle/.venv/lib/collections
to point to the venv.
The moment I removed all those Collections from /usr/share
, one single part of this playbook ran into the following error:
fatal: [host01]: FAILED! => changed=false
msg: Failed to import the required Python library (firewall) on host01's Python /home/ansible/ansible-oracle/.venv/bin/python3.9. Please read the module documentation and install it in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter. Version 0.2.11 or newer required (0.3.9 or newer for offline operations)
The strange thing is, there are other Galaxy Collections used in the playbook (e.g., community.general) which do just fine. The only task that breaks is the one with ansible.posix.firewalld
.
The playbook is as simple as this:
- hosts: "{{ hostgroup | default('all') }}"
become: yes
roles:
- basic-linux
And here's the task causing the error:
- name: allow connections on oracle database default port
ansible.posix.firewalld:
port: 1521/tcp
permanent: true
immediate: true
state: enabled
Check for a possible version mismatch:
$ ansible --version | grep "python version"
python version = 3.9.19 (main, Aug 23 2024, 00:00:00) [GCC 11.5.0 20240719 (Red Hat 11.5.0-2.0.1)] (/usr/bin/python3)
$ python --version
Python 3.9.19
$ /usr/bin/python --version
Python 3.9.19
When I install the Collections globally (read: /usr/share/...), the task runs just fine. But I need a self-contained environment that doesn't break when someone upgrades something global.
While researching I stumbled upon this suggestion and modified the task to look like this (last 2 lines added):
- name: allow connections on oracle database default port
ansible.posix.firewalld:
port: 1521/tcp
permanent: true
immediate: true
state: enabled
vars:
ansible_python_interpreter: /usr/bin/python
Then it runs, but why?
As the Ansible host and the target host are one and the same, the Python versions used are also identical. This even applies to the venv for now, because Python in the venv was installed from the same source (the OS install ISO image).
Also, when the Collections are installed in /usr/share, they would still use the same Python version, or not? And why is only ansible.posix.firewalld
affected but not community.general.*
?
Before you point me to the loads of answers to similar questions: I've reviewed what I could find, but there never was a fitting explanation for my particular issue. That is: the moment ansible_python_interpreter: /usr/bin/python
is set instead of the default python3
, it works.
Edit: My best guess is that this has to do with the host connection being defined as 'local':
---
all:
children:
test:
hosts:
host01:
ansible_host: local
ansible_connection: local
When I change it to an SSH connection it runs without said error as well.
Could it be that on "local" connections, the venv settings are used while running in the target context? That would be sub-optimal.
Can anyone confirm my guess? If so, can the local connect be changed to behave like the ssh connect (e.g., use python from $PATH)?
Share Improve this question edited Feb 18 at 8:46 Uwe asked Feb 17 at 19:22 UweUwe 214 bronze badges 2- Can you further isolate the problem? In particular, I wonder if you can reproduce it in a local venv. See also minimal reproducible example. – Ulrich Eckhardt Commented Feb 18 at 8:52
- Thanks @UlrichEckhardt, I checked a bit more on the topic of SSH vs. local connections and think I found the difference in behaviour and a mitigation. Will post an answer to myself shortly. – Uwe Commented 2 days ago
1 Answer
Reset to default 2Well... looks like this is yet another example of "communicating the problem helps understanding it". I was already on the right track with my assumption that local connections are treated differently than remote ones.
After reading the Ansible docs more closely (namely Running against localhost and Controlling where tasks run), it became clear to me that on local connections, Ansible tries to run the same Python interpreter as in its own (virtual) environment.
Additionally to my error message above, this can also result in a "permission denied" when Ansible becomes another user that hasn't permissions to files in the (virtual) environment of the Ansible user.
Solution:
Configure the host in the inventory with an explicit setting for the Python interpreter to use. E. g., "/usr/bin/env python"
or "{{ ansible_playbook_python }}"
.
In my example above, this runs with the host entry:
---
all:
children:
test:
hosts:
host01:
ansible_host: local
ansible_connection: local
ansible_python_interpreter: "{{ ansible_playbook_python }}"