Skip to content

Commit 177947a

Browse files
Add support for the SSH ControlPath connection sharing option (#713)
OpenSSH multiplexing needs 3 configuration items: * ControlMaster * ControlPersist * ControlPath Testinfra uses only the first two and this causes that connection multiplexing is not used: ``` DEBUG testinfra:base.py:288 RUN CommandResult(command=b'ssh -o ControlMaster=auto -o ControlPersist=1200 -o ServerAliveInterval=180 -o StrictHostKeyChecking=no -o User=<reducted> -i \'~/.ssh/<reducted>\' -o ConnectTimeout=10 <reducted> \'sudo /bin/sh -c \'"\'"\'<reducted>\'"\'"\'\'', exit_status=0, stdout=b'<reducted>', stderr=None) ``` In comparison, Ansible uses all 3 parameters: ``` SSH: EXEC ssh -o ControlMaster=auto -o ControlPersist=1200 -o ServerAliveInterval=180 -o StrictHostKeyChecking=no -o StrictHostKeyChecking=no -o 'IdentityFile="<reducted>"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="<reducted>"' -o ConnectTimeout=10 -o 'ControlPath="<reducted>/.ansible/cp/%C"' <reducted> ``` With this change the third option to control connection sharing is introduced.
1 parent a12badf commit 177947a

File tree

2 files changed

+18
-0
lines changed

2 files changed

+18
-0
lines changed

testinfra/backend/ssh.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(
2727
ssh_config: Optional[str] = None,
2828
ssh_identity_file: Optional[str] = None,
2929
timeout: int = 10,
30+
controlpath: str = "",
3031
controlpersist: int = 60,
3132
ssh_extra_args: Optional[str] = None,
3233
*args: Any,
@@ -36,6 +37,7 @@ def __init__(
3637
self.ssh_config = ssh_config
3738
self.ssh_identity_file = ssh_identity_file
3839
self.timeout = int(timeout)
40+
self.controlpath = controlpath
3941
self.controlpersist = int(controlpersist)
4042
self.ssh_extra_args = ssh_extra_args
4143
super().__init__(self.host.name, *args, **kwargs)
@@ -75,6 +77,12 @@ def _build_ssh_command(self, command: str) -> tuple[list[str], list[str]]:
7577
self.controlpersist
7678
)
7779
)
80+
if (
81+
"ControlMaster" in " ".join(cmd)
82+
and self.controlpath
83+
and ("controlpath" not in (self.ssh_extra_args or "").lower())
84+
):
85+
cmd.append("-o ControlPath={}".format(self.controlpath))
7886
cmd.append("%s %s")
7987
cmd_args.extend([self.host.name, command])
8088
return cmd, cmd_args

testinfra/utils/ansible_runner.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,16 @@ def get_config(
185185
]
186186
).strip()
187187

188+
control_path = config.get("ssh_connection", "control_path", fallback="", raw=True)
189+
if control_path:
190+
directory = config.get(
191+
"persistent_connection", "control_path_dir", fallback="~/.ansible/cp"
192+
)
193+
control_path = control_path % ({"directory": directory}) # noqa: S001
194+
# restore original "%%"
195+
control_path = control_path.replace("%", "%%")
196+
kwargs["controlpath"] = control_path
197+
188198
spec = "{}://".format(connection)
189199

190200
# Fallback to user:password auth when identity file is not used

0 commit comments

Comments
 (0)