diff --git a/changelogs/fragments/82954-fix-older-connection-plugins.yml b/changelogs/fragments/82954-fix-older-connection-plugins.yml new file mode 100644 index 00000000000..01324a39f86 --- /dev/null +++ b/changelogs/fragments/82954-fix-older-connection-plugins.yml @@ -0,0 +1,2 @@ +bugfixes: + - Fix check for missing _sub_plugin attribute in older connection plugins (https://github.com/ansible/ansible/pull/82954) diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index 2d615e29d90..a8e24f5f36a 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -1091,7 +1091,7 @@ class TaskExecutor: # deals with networking sub_plugins (network_cli/httpapi/netconf) sub = getattr(self._connection, '_sub_plugin', None) - if sub is not None and sub.get('type') != 'external': + if sub and sub.get('type') != 'external': plugin_type = get_plugin_class(sub.get("obj")) varnames.extend(self._set_plugin_options(plugin_type, variables, templar, task_keys)) sub_conn = getattr(self._connection, 'ssh_type_conn', None) diff --git a/test/integration/targets/connection_local/connection_plugins/network_noop.py b/test/integration/targets/connection_local/connection_plugins/network_noop.py new file mode 100644 index 00000000000..5b0c5842acd --- /dev/null +++ b/test/integration/targets/connection_local/connection_plugins/network_noop.py @@ -0,0 +1,95 @@ +# (c) 2024 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import annotations + +DOCUMENTATION = """ +connection: network_noop +author: ansible-core +short_description: legacy-ish connection plugin with only minimal config +description: + - A wrapper around NetworkConnectionBase to test that the default attributes don't cause internal errors in TE. +options: + persistent_log_messages: + type: boolean + description: + - This flag will enable logging the command executed and response received from + target device in the ansible log file. For this option to work 'log_path' ansible + configuration option is required to be set to a file path with write access. + - Be sure to fully understand the security implications of enabling this + option as it could create a security vulnerability by logging sensitive information in log file. + default: False + ini: + - section: persistent_connection + key: log_messages + env: + - name: ANSIBLE_PERSISTENT_LOG_MESSAGES + vars: + - name: ansible_persistent_log_messages + persistent_command_timeout: + type: int + description: + - Configures, in seconds, the amount of time to wait for a command to + return from the remote device. If this timer is exceeded before the + command returns, the connection plugin will raise an exception and + close. + default: 30 + ini: + - section: persistent_connection + key: command_timeout + env: + - name: ANSIBLE_PERSISTENT_COMMAND_TIMEOUT + vars: + - name: ansible_command_timeout + persistent_connect_timeout: + type: int + description: + - Configures, in seconds, the amount of time to wait when trying to + initially establish a persistent connection. If this value expires + before the connection to the remote device is completed, the connection + will fail. + default: 30 + ini: + - section: persistent_connection + key: connect_timeout + env: + - name: ANSIBLE_PERSISTENT_CONNECT_TIMEOUT + vars: +extends_documentation_fragment: + - connection_pipelining +""" + +from ansible.plugins.connection import NetworkConnectionBase, ensure_connect +from ansible.utils.display import Display + +display = Display() + + +class Connection(NetworkConnectionBase): + transport = 'network_noop' + has_pipelining = True + + def __init__(self, play_context, new_stdin, *args, **kwargs): + super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) + + @ensure_connect + def exec_command(self, *args, **kwargs): + return super(Connection, self).exec_command(*args, **kwargs) + + @ensure_connect + def put_file(self, *args, **kwargs): + return super(Connection, self).put_file(*args, **kwargs) + + @ensure_connect + def fetch_file(self, *args, **kwargs): + return super(Connection, self).fetch_file(*args, **kwargs) + + def _connect(self): + if not self.connected: + self._connected = True + display.vvv("ESTABLISH NEW CONNECTION") + + def close(self): + if self.connected: + display.vvv("CLOSING CONNECTION") + super(Connection, self).close() diff --git a/test/integration/targets/connection_local/runme.sh b/test/integration/targets/connection_local/runme.sh index a2c32adf06b..42b2b827200 100755 --- a/test/integration/targets/connection_local/runme.sh +++ b/test/integration/targets/connection_local/runme.sh @@ -12,3 +12,10 @@ INVENTORY="../connection_${group}/test_connection.inventory" ./test.sh \ -e local_tmp=/tmp/ansible-local \ -e remote_tmp=/tmp/ansible-remote \ "$@" + +ANSIBLE_CONNECTION_PLUGINS="../connection_${group}/connection_plugins" INVENTORY="../connection_${group}/test_network_connection.inventory" ./test.sh \ + -e target_hosts="${group}" \ + -e action_prefix= \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=/tmp/ansible-remote \ + "$@" diff --git a/test/integration/targets/connection_local/test_network_connection.inventory b/test/integration/targets/connection_local/test_network_connection.inventory new file mode 100644 index 00000000000..8114023349a --- /dev/null +++ b/test/integration/targets/connection_local/test_network_connection.inventory @@ -0,0 +1,2 @@ +[all] +local ansible_host=127.0.0.1 ansible_connection=network_noop