From 59b6126320aa19d33c2e02665c21a5f6b7f08a32 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 12:00:12 +0100 Subject: [PATCH] [PR #11204/6ae47590 backport][stable-12] lxc_container: replace subprocess.Popen() with run_command() (#11339) lxc_container: replace subprocess.Popen() with run_command() (#11204) * lxc_container: replace subprocess.Popen() with run_command() * Update plugins/modules/lxc_container.py * add changelog frag * retain Popen logic in module_utils * Update plugins/module_utils/_lxc.py --------- (cherry picked from commit 6ae47590cda8883e8751a2df08e805a92fa167e7) Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> Co-authored-by: Felix Fontein --- .github/BOTMETA.yml | 6 +- .../fragments/11204-lxc-container-popen.yml | 2 + plugins/module_utils/_lxc.py | 61 ++++++++++++++++++ plugins/modules/lxc_container.py | 62 +------------------ tests/sanity/ignore-2.17.txt | 1 - tests/sanity/ignore-2.18.txt | 1 - tests/sanity/ignore-2.19.txt | 1 - tests/sanity/ignore-2.20.txt | 1 - tests/sanity/ignore-2.21.txt | 1 - 9 files changed, 69 insertions(+), 67 deletions(-) create mode 100644 changelogs/fragments/11204-lxc-container-popen.yml create mode 100644 plugins/module_utils/_lxc.py diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 58c82c2859..2cd7bbeabe 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -368,6 +368,8 @@ files: $module_utils/jenkins.py: labels: jenkins maintainers: russoz + $module_utils/_lxc.py: + maintainers: russoz $module_utils/manageiq.py: labels: manageiq maintainers: $team_manageiq @@ -1504,7 +1506,7 @@ files: maintainers: vbotka $modules/sssd_info.py: maintainers: a-gabidullin -######################### + ######################### docs/docsite/rst/filter_guide.rst: {} docs/docsite/rst/filter_guide_abstract_informations.rst: {} docs/docsite/rst/filter_guide_abstract_informations_counting_elements_in_sequence.rst: @@ -1573,7 +1575,7 @@ files: maintainers: russoz docs/docsite/rst/test_guide.rst: maintainers: felixfontein -######################### + ######################### tests/: labels: tests tests/integration: diff --git a/changelogs/fragments/11204-lxc-container-popen.yml b/changelogs/fragments/11204-lxc-container-popen.yml new file mode 100644 index 0000000000..9056fed4f6 --- /dev/null +++ b/changelogs/fragments/11204-lxc-container-popen.yml @@ -0,0 +1,2 @@ +minor_changes: + - lxc_container - refactor function ``create_script``, using ``subprocess.Popen()``, to a new module_utils ``_lxc`` (https://github.com/ansible-collections/community.general/pull/11204). diff --git a/plugins/module_utils/_lxc.py b/plugins/module_utils/_lxc.py new file mode 100644 index 0000000000..4e7e0ef759 --- /dev/null +++ b/plugins/module_utils/_lxc.py @@ -0,0 +1,61 @@ +# Copyright (c) 2014, Kevin Carter +# Copyright (c) 2025, Alexei Znamensky +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import annotations + +import os +import subprocess +import tempfile + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_bytes + + +# This is used to attach to a running container and execute commands from +# within the container on the host. This will provide local access to a +# container without using SSH. The template will attempt to work within the +# home directory of the user that was attached to the container and source +# that users environment variables by default. +ATTACH_TEMPLATE = """#!/usr/bin/env bash +pushd "$(getent passwd $(whoami)|cut -f6 -d':')" + if [[ -f ".bashrc" ]];then + source .bashrc + unset HOSTNAME + fi +popd + +# User defined command +{} +""" + + +def create_script(command: str, module: AnsibleModule) -> None: + """Write out a script onto a target. + + This method should be backward compatible with Python when executing + from within the container. + + :param command: command to run, this can be a script and can use spacing + with newlines as separation. + :param module: AnsibleModule to run commands with. + """ + + script_file = "" + try: + f = tempfile.NamedTemporaryFile(prefix="lxc-attach-script", delete=False, mode="wb") + f.write(to_bytes(ATTACH_TEMPLATE.format(command), errors="surrogate_or_strict")) + script_file = f.name + f.flush() + f.close() + + os.chmod(script_file, 0o0700) + + with tempfile.NamedTemporaryFile(prefix="lxc-attach-script-log", delete=False, mode="ab") as stdout_file: + with tempfile.NamedTemporaryFile(prefix="lxc-attach-script-err", delete=False, mode="ab") as stderr_file: + subprocess.Popen([script_file], stdout=stdout_file, stderr=stderr_file).communicate() + + finally: + if script_file: + os.remove(script_file) diff --git a/plugins/modules/lxc_container.py b/plugins/modules/lxc_container.py index 1a13905f6a..ab28243e52 100644 --- a/plugins/modules/lxc_container.py +++ b/plugins/modules/lxc_container.py @@ -408,7 +408,6 @@ lxc_container: import os import re -import subprocess import tempfile import time import shlex @@ -424,6 +423,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE from ansible.module_utils.common.text.converters import to_text, to_bytes +from ansible_collections.community.general.plugins.module_utils._lxc import create_script # LXC_COMPRESSION_MAP is a map of available compression types when creating # an archive of a container. @@ -504,64 +504,6 @@ LXC_ANSIBLE_STATES = { } -# This is used to attach to a running container and execute commands from -# within the container on the host. This will provide local access to a -# container without using SSH. The template will attempt to work within the -# home directory of the user that was attached to the container and source -# that users environment variables by default. -ATTACH_TEMPLATE = """#!/usr/bin/env bash -pushd "$(getent passwd $(whoami)|cut -f6 -d':')" - if [[ -f ".bashrc" ]];then - source .bashrc - unset HOSTNAME - fi -popd - -# User defined command -%(container_command)s -""" - - -def create_script(command): - """Write out a script onto a target. - - This method should be backward compatible with Python when executing - from within the container. - - :param command: command to run, this can be a script and can use spacing - with newlines as separation. - :type command: ``str`` - """ - - (fd, script_file) = tempfile.mkstemp(prefix="lxc-attach-script") - f = os.fdopen(fd, "wb") - try: - f.write(to_bytes(ATTACH_TEMPLATE % {"container_command": command}, errors="surrogate_or_strict")) - f.flush() - finally: - f.close() - - # Ensure the script is executable. - os.chmod(script_file, int("0700", 8)) - - # Output log file. - stdout_file = os.fdopen(tempfile.mkstemp(prefix="lxc-attach-script-log")[0], "ab") - - # Error log file. - stderr_file = os.fdopen(tempfile.mkstemp(prefix="lxc-attach-script-err")[0], "ab") - - # Execute the script command. - try: - subprocess.Popen([script_file], stdout=stdout_file, stderr=stderr_file).communicate() - finally: - # Close the log files. - stderr_file.close() - stdout_file.close() - - # Remove the script file upon completion of execution. - os.remove(script_file) - - class LxcContainerManagement: def __init__(self, module): """Management of LXC containers via Ansible. @@ -865,7 +807,7 @@ class LxcContainerManagement: elif container_state == "stopped": self._container_startup() - self.container.attach_wait(create_script, container_command) + self.container.attach_wait(create_script, (container_command, self.module)) self.state_change = True def _container_startup(self, timeout=60): diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index b37d75a1f5..b96f5d20fc 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -2,7 +2,6 @@ plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choic plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/homectl.py import-3.12 # Uses deprecated stdlib library 'crypt' plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin -plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice diff --git a/tests/sanity/ignore-2.18.txt b/tests/sanity/ignore-2.18.txt index b37d75a1f5..b96f5d20fc 100644 --- a/tests/sanity/ignore-2.18.txt +++ b/tests/sanity/ignore-2.18.txt @@ -2,7 +2,6 @@ plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choic plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/homectl.py import-3.12 # Uses deprecated stdlib library 'crypt' plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin -plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice diff --git a/tests/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt index f6b058ec69..561494d681 100644 --- a/tests/sanity/ignore-2.19.txt +++ b/tests/sanity/ignore-2.19.txt @@ -2,7 +2,6 @@ plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choic plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/homectl.py import-3.12 # Uses deprecated stdlib library 'crypt' plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin -plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice diff --git a/tests/sanity/ignore-2.20.txt b/tests/sanity/ignore-2.20.txt index f6b058ec69..561494d681 100644 --- a/tests/sanity/ignore-2.20.txt +++ b/tests/sanity/ignore-2.20.txt @@ -2,7 +2,6 @@ plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choic plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/homectl.py import-3.12 # Uses deprecated stdlib library 'crypt' plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin -plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice diff --git a/tests/sanity/ignore-2.21.txt b/tests/sanity/ignore-2.21.txt index 1ecb499e8b..9afccd20f1 100644 --- a/tests/sanity/ignore-2.21.txt +++ b/tests/sanity/ignore-2.21.txt @@ -7,7 +7,6 @@ plugins/modules/interfaces_file.py validate-modules:bad-return-value-key # TODO plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin plugins/modules/keycloak_realm_info.py validate-modules:bad-return-value-key # TODO: rename offending return values if possible, or adjust this comment in case the name is OK plugins/modules/keycloak_realm_keys_metadata_info.py validate-modules:bad-return-value-key # TODO: rename offending return values if possible, or adjust this comment in case the name is OK -plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/nosh.py validate-modules:bad-return-value-key # TODO: rename offending return values if possible, or adjust this comment in case the name is OK plugins/modules/omapi_host.py validate-modules:bad-return-value-key # TODO: rename offending return values if possible, or adjust this comment in case the name is OK plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice