diff --git a/changelogs/fragments/460-respawn.yaml b/changelogs/fragments/460-respawn.yaml new file mode 100644 index 0000000..b88763b --- /dev/null +++ b/changelogs/fragments/460-respawn.yaml @@ -0,0 +1,10 @@ +--- +minor_changes: + - "seboolean - respawn module to use the system python interpreter when the ``selinux`` python module is not available for ``ansible_python_interpreter`` + (https://github.com/ansible-collections/ansible.posix/pull/460)." + - "selinux - respawn module to use the system python interpreter when the ``selinux`` python module is not available for ``ansible_python_interpreter`` + (https://github.com/ansible-collections/ansible.posix/pull/460)." + - "firewalld - respawn module to use the system python interpreter when the ``firewall`` python module is not available for ``ansible_python_interpreter`` + (https://github.com/ansible-collections/ansible.posix/pull/460)." + - "firewalld_info - respawn module to use the system python interpreter when the ``firewall`` python module is not available for ``ansible_python_interpreter`` + (https://github.com/ansible-collections/ansible.posix/pull/460)." diff --git a/plugins/module_utils/_respawn.py b/plugins/module_utils/_respawn.py new file mode 100644 index 0000000..55abaf5 --- /dev/null +++ b/plugins/module_utils/_respawn.py @@ -0,0 +1,45 @@ +# Copyright (c) 2023 Maxwell G +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +Helpers to respawn a module to run using the system interpreter +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +try: + from ansible.module_utils.common import respawn +except ImportError: + HAS_RESPAWN_UTIL = False +else: + HAS_RESPAWN_UTIL = True + + +SYSTEM_PYTHON_INTERPRETERS = ( + "/usr/bin/libexec/platform-python", + "/usr/bin/python3", + "/usr/bin/python2", + "/usr/bin/python", +) + + +def respawn_module(module): + """ + Respawn an ansible module to using the first interpreter in + SYSTEM_PYTHON_INTERPRETERS that contains `module`. + + Args: + module (str): Name of python module to search for + + Returns: + Returns None if the module cannot be respawned. + """ + if respawn.has_respawned(): + return + interpreter = respawn.probe_interpreters_for_module( + SYSTEM_PYTHON_INTERPRETERS, module + ) + if interpreter: + respawn.respawn_module(interpreter) diff --git a/plugins/module_utils/firewalld.py b/plugins/module_utils/firewalld.py index 6a76c32..011b140 100644 --- a/plugins/module_utils/firewalld.py +++ b/plugins/module_utils/firewalld.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function from ansible_collections.ansible.posix.plugins.module_utils.version import LooseVersion +from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL from ansible.module_utils.basic import missing_required_lib __metaclass__ = type @@ -314,6 +315,8 @@ class FirewallTransaction(object): installed version (%s) likely too old. Requires firewalld >= 0.2.11" % FW_VERSION) if import_failure: + if HAS_RESPAWN_UTIL: + respawn_module("firewall") module.fail_json( msg=missing_required_lib('firewall') + '. Version 0.2.11 or newer required (0.3.9 or newer for offline operations)' ) diff --git a/plugins/modules/firewalld_info.py b/plugins/modules/firewalld_info.py index 334518d..8b5c80c 100644 --- a/plugins/modules/firewalld_info.py +++ b/plugins/modules/firewalld_info.py @@ -211,6 +211,7 @@ firewalld_info: from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils._text import to_native +from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL from ansible_collections.ansible.posix.plugins.module_utils.version import StrictVersion @@ -322,6 +323,12 @@ def main(): ) # Exit with failure message if requirements modules are not installed. + if not HAS_DBUS and not HAS_FIREWALLD and HAS_RESPAWN_UTIL: + # Only respawn the module if both libraries are missing. + # If only one is available, then usage of the "wrong" (i.e. not the system one) + # python interpreter is likely not the problem. + respawn_module("firewall") + if not HAS_DBUS: module.fail_json(msg=missing_required_lib('python-dbus')) if not HAS_FIREWALLD: diff --git a/plugins/modules/seboolean.py b/plugins/modules/seboolean.py index 657b7fa..0d23073 100644 --- a/plugins/modules/seboolean.py +++ b/plugins/modules/seboolean.py @@ -75,6 +75,7 @@ except ImportError: from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.six import binary_type from ansible.module_utils._text import to_bytes, to_text +from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL def get_runtime_status(ignore_selinux_state=False): @@ -281,6 +282,12 @@ def main(): supports_check_mode=True, ) + if not HAVE_SELINUX and not HAVE_SEMANAGE and HAS_RESPAWN_UTIL: + # Only respawn the module if both libraries are missing. + # If only one is available, then usage of the "wrong" (i.e. not the system one) + # python interpreter is likely not the problem. + respawn_module("selinux") + if not HAVE_SELINUX: module.fail_json(msg=missing_required_lib('libselinux-python'), exception=SELINUX_IMP_ERR) diff --git a/plugins/modules/selinux.py b/plugins/modules/selinux.py index 14110fe..0609462 100644 --- a/plugins/modules/selinux.py +++ b/plugins/modules/selinux.py @@ -107,6 +107,8 @@ from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.common.process import get_bin_path from ansible.module_utils.facts.utils import get_file_lines +from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL + # getter subroutines def get_config_state(configfile): @@ -236,6 +238,8 @@ def main(): ) if not HAS_SELINUX: + if HAS_RESPAWN_UTIL: + respawn_module("selinux") module.fail_json(msg=missing_required_lib('libselinux-python'), exception=SELINUX_IMP_ERR) # global vars diff --git a/tests/integration/targets/firewalld/tasks/main.yml b/tests/integration/targets/firewalld/tasks/main.yml index 17f14c2..5f81c62 100644 --- a/tests/integration/targets/firewalld/tasks/main.yml +++ b/tests/integration/targets/firewalld/tasks/main.yml @@ -10,11 +10,6 @@ state: present # This doesn't work for CentOS 6 because firewalld doesn't exist in CentOS6 - - name: Check to make sure the firewalld python module is available. - shell: "{{ansible_python.executable}} -c 'import firewall'" - register: check_output - ignore_errors: true - - name: Enable dbus-broker daemon service: name: dbus-broker @@ -30,7 +25,6 @@ state: started - import_tasks: run_all_tests.yml - when: check_output.rc == 0 - name: Test Offline Operations block: @@ -40,7 +34,6 @@ state: stopped - import_tasks: run_all_tests.yml - when: check_output.rc == 0 when: - ansible_facts.os_family == "RedHat" and ansible_facts.distribution_major_version is version('7', '>=')