diff options
Diffstat (limited to 'tests/topotests/munet/base.py')
| -rw-r--r-- | tests/topotests/munet/base.py | 139 |
1 files changed, 102 insertions, 37 deletions
diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py index eb4b088442..72b5df54b9 100644 --- a/tests/topotests/munet/base.py +++ b/tests/topotests/munet/base.py @@ -21,7 +21,6 @@ import subprocess import sys import tempfile import time as time_mod - from collections import defaultdict from pathlib import Path from typing import Union @@ -29,10 +28,8 @@ from typing import Union from . import config as munet_config from . import linux - try: import pexpect - from pexpect.fdpexpect import fdspawn from pexpect.popen_spawn import PopenSpawn @@ -47,6 +44,9 @@ root_hostname = subprocess.check_output("hostname") our_pid = os.getpid() +detailed_cmd_logging = False + + class MunetError(Exception): """A generic munet error.""" @@ -119,6 +119,27 @@ def cmd_error(rc, o, e): return s + o + e +def shorten(s): + s = s.strip() + i = s.find("\n") + if i > 0: + s = s[: i - 1] + if not s.endswith("..."): + s += "..." + if len(s) > 72: + s = s[:69] + if not s.endswith("..."): + s += "..." + return s + + +def comm_result(rc, o, e): + s = f"\n\treturncode {rc}" if rc else "" + o = "\n\tstdout: " + shorten(o) if o and o.strip() else "" + e = "\n\tstderr: " + shorten(e) if e and e.strip() else "" + return s + o + e + + def proc_str(p): if hasattr(p, "args"): args = p.args if isinstance(p.args, str) else " ".join(p.args) @@ -477,16 +498,26 @@ class Commander: # pylint: disable=R0904 defaults["preexec_fn"] = os.setsid defaults["env"]["PS1"] = "$ " - self.logger.debug( - '%s: %s %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)', - self, - "XXX" if method == "_spawn" else "", - method, - cmd_list, - pre_cmd_list if not skip_pre_cmd else "", - use_pty, - defaults, - ) + if not detailed_cmd_logging: + pre_cmd_str = shlex.join(pre_cmd_list) if not skip_pre_cmd else "" + if "nsenter" in pre_cmd_str: + self.logger.debug('%s("%s")', method, shlex.join(cmd_list)) + elif pre_cmd_str: + self.logger.debug( + '%s("%s") [precmd: %s]', method, shlex.join(cmd_list), pre_cmd_str + ) + else: + self.logger.debug('%s("%s") [no precmd]', method, shlex.join(cmd_list)) + else: + self.logger.debug( + '%s: %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)', + self, + method, + cmd_list, + pre_cmd_list if not skip_pre_cmd else "", + use_pty, + defaults, + ) actual_cmd_list = cmd_list if skip_pre_cmd else pre_cmd_list + cmd_list return actual_cmd_list, defaults @@ -531,7 +562,7 @@ class Commander: # pylint: disable=R0904 def _spawn(self, cmd, skip_pre_cmd=False, use_pty=False, echo=False, **kwargs): logging.debug( - '%s: XXX _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s', + '%s: _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s', self, cmd, skip_pre_cmd, @@ -544,7 +575,7 @@ class Commander: # pylint: disable=R0904 ) self.logger.debug( - '%s: XXX %s("%s", use_pty %s echo %s defaults: %s)', + '%s: %s("%s", use_pty %s echo %s defaults: %s)', self, "PopenSpawn" if not use_pty else "pexpect.spawn", actual_cmd, @@ -830,14 +861,18 @@ class Commander: # pylint: disable=R0904 else: o, e = await p.communicate() self.logger.debug( - "%s: cmd_p already exited status: %s", self, proc_error(p, o, e) + "%s: [cleanup_proc] proc already exited status: %s", + self, + proc_error(p, o, e), ) return None if pid is None: pid = p.pid - self.logger.debug("%s: terminate process: %s (pid %s)", self, proc_str(p), pid) + self.logger.debug( + "%s: [cleanup_proc] terminate process: %s (pid %s)", self, proc_str(p), pid + ) try: # This will SIGHUP and wait a while then SIGKILL and return immediately await self.cleanup_pid(p.pid, pid) @@ -850,14 +885,19 @@ class Commander: # pylint: disable=R0904 else: o, e = await asyncio.wait_for(p.communicate(), timeout=wait_secs) self.logger.debug( - "%s: cmd_p exited after kill, status: %s", self, proc_error(p, o, e) + "%s: [cleanup_proc] exited after kill, status: %s", + self, + proc_error(p, o, e), ) except (asyncio.TimeoutError, subprocess.TimeoutExpired): - self.logger.warning("%s: SIGKILL timeout", self) + self.logger.warning("%s: [cleanup_proc] SIGKILL timeout", self) return p except Exception as error: self.logger.warning( - "%s: kill unexpected exception: %s", self, error, exc_info=True + "%s: [cleanup_proc] kill unexpected exception: %s", + self, + error, + exc_info=True, ) return p return None @@ -873,7 +913,11 @@ class Commander: # pylint: disable=R0904 def _cmd_status_finish(self, p, c, ac, o, e, raises, warn): rc = p.returncode self.last = (rc, ac, c, o, e) - if rc: + if not rc: + resstr = comm_result(rc, o, e) + if resstr: + self.logger.debug("%s", resstr) + else: if warn: self.logger.warning("%s: proc failed: %s", self, proc_error(p, o, e)) if raises: @@ -1167,7 +1211,7 @@ class Commander: # pylint: disable=R0904 # XXX need to test ssh in Xterm sudo_path = get_exec_path_host(["sudo"]) # This first test case seems same as last but using list instead of string? - if self.is_vm and self.use_ssh: # pylint: disable=E1101 + if self.is_vm and self.use_ssh and not ns_only: # pylint: disable=E1101 if isinstance(cmd, str): cmd = shlex.split(cmd) cmd = ["/usr/bin/env", f"MUNET_NODENAME={self.name}"] + cmd @@ -1187,8 +1231,14 @@ class Commander: # pylint: disable=R0904 else: # This is the command to execute to be inside the namespace. # We are getting into trouble with quoting. - # Why aren't we passing in MUNET_RUNDIR? - cmd = f"/usr/bin/env MUNET_NODENAME={self.name} {cmd}" + envvars = f"MUNET_NODENAME={self.name} NODENAME={self.name}" + if hasattr(self, "rundir"): + envvars += f" RUNDIR={self.rundir}" + if hasattr(self.unet, "config_dirname") and self.unet.config_dirname: + envvars += f" CONFIGDIR={self.unet.config_dirname}" + elif "CONFIGDIR" in os.environ: + envvars += f" CONFIGDIR={os.environ['CONFIGDIR']}" + cmd = f"/usr/bin/env {envvars} {cmd}" # We need sudo b/c we are executing as the user inside the window system. sudo_path = get_exec_path_host(["sudo"]) nscmd = ( @@ -1241,9 +1291,13 @@ class Commander: # pylint: disable=R0904 # XXX not appropriate for ssh cmd = ["sudo", "-Eu", os.environ["SUDO_USER"]] + cmd - if not isinstance(nscmd, str): - nscmd = shlex.join(nscmd) - cmd.append(nscmd) + if title: + cmd.append("-t") + cmd.append(title) + + if isinstance(nscmd, str): + nscmd = shlex.split(nscmd) + cmd.extend(nscmd) elif "DISPLAY" in os.environ: cmd = [get_exec_path_host("xterm")] if "SUDO_USER" in os.environ: @@ -1294,6 +1348,14 @@ class Commander: # pylint: disable=R0904 "select-layout", "-t", pane_info if not tmux_target else tmux_target, + "even-horizontal", + ] + commander.cmd_status(cmd) + cmd = [ + get_exec_path_host("tmux"), + "select-layout", + "-t", + pane_info if not tmux_target else tmux_target, "tiled", ] commander.cmd_status(cmd) @@ -1872,18 +1934,19 @@ class LinuxNamespace(Commander, InterfaceMixin): assert unet is None self.uflags = uflags # - # Open file descriptors for current namespaces for later resotration. + # Open file descriptors for current namespaces for later restoration. # try: + # pidfd_open is actually present in 5.4, is this 5.8 check for another + # aspect of what the pidfd_open code is relying on, something in the + # namespace code? If not we can simply check for os.pidfd_open() being + # present as our compat module linux.py runtime patches it in if + # supported by the kernel. kversion = [int(x) for x in platform.release().split("-")[0].split(".")] kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 8) except ValueError: kvok = False - if ( - not kvok - or sys.version_info[0] < 3 - or (sys.version_info[0] == 3 and sys.version_info[1] < 9) - ): + if not kvok: # get list of namespace file descriptors before we unshare self.p_ns_fds = [] self.p_ns_fnames = [] @@ -1962,8 +2025,10 @@ class LinuxNamespace(Commander, InterfaceMixin): stdout=stdout, stderr=stderr, text=True, - start_new_session=not unet, shell=False, + # start_new_session=not unet + # preexec_fn=os.setsid if not unet else None, + preexec_fn=os.setsid, ) # The pid number returned is in the global pid namespace. For unshare_inline @@ -2302,14 +2367,14 @@ class LinuxNamespace(Commander, InterfaceMixin): and self.pid != our_pid ): self.logger.debug( - "cleanup pid on separate pid %s from proc pid %s", + "cleanup separate pid %s from namespace proc pid %s", self.pid, self.p.pid if self.p else None, ) await self.cleanup_pid(self.pid) if self.p is not None: - self.logger.debug("cleanup proc pid %s", self.p.pid) + self.logger.debug("cleanup namespace proc pid %s", self.p.pid) await self.async_cleanup_proc(self.p) # return to the previous namespace, need to do this in case anothe munet @@ -2894,7 +2959,7 @@ if True: # pylint: disable=using-constant-test ) logging.debug( - 'ShellWraper: XXX prompt "%s" will_echo %s child.echo %s', + 'ShellWraper: prompt "%s" will_echo %s child.echo %s', prompt, will_echo, spawn.echo, |
