diff --git a/changelogs/fragments/control_path.yml b/changelogs/fragments/control_path.yml new file mode 100644 index 00000000000..16190a08701 --- /dev/null +++ b/changelogs/fragments/control_path.yml @@ -0,0 +1,4 @@ +--- +bugfixes: + - connection - use inventory_hostname while generating control path directory name to make it more unique (https://github.com/ansible/ansible/issues/76956). + diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index ebef9cbfd15..8585454099e 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -993,7 +993,8 @@ class TaskExecutor: self._play_context, self._new_stdin, task_uuid=self._task._uuid, - ansible_playbook_pid=to_text(os.getppid()) + ansible_playbook_pid=to_text(os.getppid()), + inventory_hostname=cvars.get("inventory_hostname") ) if not connection: diff --git a/lib/ansible/plugins/connection/__init__.py b/lib/ansible/plugins/connection/__init__.py index de4a79e9818..d76d14fa853 100644 --- a/lib/ansible/plugins/connection/__init__.py +++ b/lib/ansible/plugins/connection/__init__.py @@ -103,6 +103,7 @@ class ConnectionBase(AnsiblePlugin): shell_type = play_context.shell if play_context.shell else getattr(self, '_shell_type', None) self._shell = get_shell_plugin(shell_type=shell_type, executable=self._play_context.executable) + self._inventory_hostname = kwargs.get("inventory_hostname") self.become: BecomeBase | None = None @property diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py index 299039faa5b..2a02b929087 100644 --- a/lib/ansible/plugins/connection/ssh.py +++ b/lib/ansible/plugins/connection/ssh.py @@ -597,13 +597,16 @@ class Connection(ConnectionBase): user: str | None, connection: ConnectionBase | None = None, pid: int | None = None, + inventory_hostname: str | None = None, ) -> str: """Make a hash for the controlpath based on con attributes""" - pstring = '%s-%s-%s' % (host, port, user) + pstring = f"{host}-{port}-{user}" if connection: - pstring += '-%s' % connection + pstring += f"-{connection}" if pid: - pstring += '-%s' % to_text(pid) + pstring += f"-{pid}" + if inventory_hostname: + pstring += f"-{inventory_hostname}" m = hashlib.sha1() m.update(to_bytes(pstring)) digest = m.hexdigest() @@ -810,7 +813,8 @@ class Connection(ConnectionBase): self.control_path = self._create_control_path( self.host, self.port, - self.user + self.user, + inventory_hostname=self._inventory_hostname, ) b_args = (b"-o", b'ControlPath="%s"' % to_bytes(self.control_path % dict(directory=cpdir), errors='surrogate_or_strict')) self._add_args(b_command, b_args, u"found only ControlPersist; added ControlPath") diff --git a/test/integration/targets/connection_ssh/control_path.inventory b/test/integration/targets/connection_ssh/control_path.inventory new file mode 100644 index 00000000000..9ae1e563e5b --- /dev/null +++ b/test/integration/targets/connection_ssh/control_path.inventory @@ -0,0 +1,9 @@ +[web] +web1 +web2 + +[web:vars] +ansible_host=localhost +ansible_connection=ssh +ansible_python_interpreter="{{ ansible_playbook_python }}" +ansible_ssh_pipelining=true diff --git a/test/integration/targets/connection_ssh/runme.sh b/test/integration/targets/connection_ssh/runme.sh index 5fee4317c1f..54a1bafbc0c 100755 --- a/test/integration/targets/connection_ssh/runme.sh +++ b/test/integration/targets/connection_ssh/runme.sh @@ -79,3 +79,9 @@ ANSIBLE_CONFIG=./test_ssh_defaults.cfg ansible-playbook verify_config.yml "$@" # ensure we handle cp with spaces correctly, otherwise would fail with # `"Failed to connect to the host via ssh: command-line line 0: keyword controlpath extra arguments at end of line"` ANSIBLE_SSH_CONTROL_PATH='/tmp/ssh cp with spaces' ansible -m ping all -e ansible_connection=ssh -i test_connection.inventory "$@" + +# Check if control path is different +ansible -m ping -i control_path.inventory web -vvv 1> stdout.txt 2> /dev/null +first=$(cat stdout.txt|grep ControlPath= | head -1) +second=$(cat stdout.txt|grep ControlPath= | tail -1) +test "$first" != "$second"