Add support for ssh multiplexing
@@ -3,6 +3,8 @@ import random
import re
import shlex
import string
+import subprocess
+import tempfile
import time
import click
@@ -214,6 +216,9 @@ class Guest(tmt.utils.Common):
class attribute '_keys' below).
"""
+ _ssh_multiplex_init_done = False
+ _ssh_socket_path = None
+
# List of supported keys
# (used for import/export to/from attributes during load and save)
_keys = ['guest', 'port', 'user', 'key', 'password']
@@ -236,7 +241,7 @@ class Guest(tmt.utils.Common):
""" Return user@guest """
return f'{self.user}@{self.guest}'
- def _ssh_options(self, join=False):
+ def _ssh_options(self, join=False, force_init_ssh_plex=False):
""" Return common ssh options (list or joined) """
options = [
'-oStrictHostKeyChecking=no',
@@ -253,19 +258,46 @@ class Guest(tmt.utils.Common):
options.extend(['-i', shlex.quote(key) if join else key])
if self.password:
options.extend(['-oPasswordAuthentication=yes'])
+
+ if not self._ssh_socket_path:
+ self._ssh_socket_path = f"/run/user/{os.getuid()}/tmt/" + next(
+ tempfile._get_candidate_names())
+
+ if force_init_ssh_plex:
+ options.extend(['-S', self._ssh_socket_path, '-MNnT'])
+ else:
+ options.extend(['-S', self._ssh_socket_path])
+
return ' '.join(options) if join else options
- def _ssh_command(self, join=False):
+ def _ssh_establish_master_connection(self):
+ """ Establish master ssh connection """
+ self._ssh_multiplex_init_done = True
+ if not self.reconnect():
+ self.warn(
+ "We are unable to establish master connection. Continue without it.")
+ return
+ os.makedirs(f"/run/user/{os.getuid()}/tmt", exist_ok=True)
+ command = self._ssh_command(
+ join=True, force_init_ssh_plex=True) + ' ' + self._ssh_guest()
+ subprocess.Popen(command, cwd=None, shell=True, env=None)
+
+ def _ssh_command(self, join=False, force_init_ssh_plex=False):
""" Prepare an ssh command line for execution (list or joined) """
+ if not self._ssh_multiplex_init_done:
+ self._ssh_establish_master_connection()
+
command = []
if self.password:
password = shlex.quote(self.password) if join else self.password
command.extend(["sshpass", "-p", password])
command.append("ssh")
if join:
- return " ".join(command) + " " + self._ssh_options(join=True)
+ return " ".join(command) + " " + self._ssh_options(join=True,
+ force_init_ssh_plex=force_init_ssh_plex)
else:
- return command + self._ssh_options()
+ return command + \
+ self._ssh_options(force_init_ssh_plex=force_init_ssh_plex)
def load(self, data):
"""
