]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/munet/mucmd.py
Merge pull request #13455 from sri-mohan1/srib-ldpd
[mirror_frr.git] / tests / topotests / munet / mucmd.py
1 # -*- coding: utf-8 eval: (blacken-mode 1) -*-
2 # SPDX-License-Identifier: GPL-2.0-or-later
3 #
4 # December 5 2021, Christian Hopps <chopps@labn.net>
5 #
6 # Copyright 2021, LabN Consulting, L.L.C.
7 #
8 """A command that allows external command execution inside nodes."""
9 import argparse
10 import json
11 import os
12 import subprocess
13 import sys
14
15 from pathlib import Path
16
17
18 def newest_file_in(filename, paths, has_sibling=None):
19 new = None
20 newst = None
21 items = (x for y in paths for x in Path(y).rglob(filename))
22 for e in items:
23 st = os.stat(e)
24 if has_sibling and not e.parent.joinpath(has_sibling).exists():
25 continue
26 if not new or st.st_mtime_ns > newst.st_mtime_ns:
27 new = e
28 newst = st
29 continue
30 return new, newst
31
32
33 def main(*args):
34 ap = argparse.ArgumentParser(args)
35 ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc")
36 ap.add_argument("node", nargs="?", help="node to enter or run command inside")
37 ap.add_argument(
38 "shellcmd",
39 nargs=argparse.REMAINDER,
40 help="optional shell-command to execute on NODE",
41 )
42 args = ap.parse_args()
43 if args.rundir:
44 configpath = Path(args.rundir).joinpath("config.json")
45 else:
46 configpath, _ = newest_file_in(
47 "config.json",
48 ["/tmp/munet", "/tmp/mutest", "/tmp/unet-test"],
49 has_sibling=args.node,
50 )
51 print(f'Using "{configpath}"')
52
53 if not configpath.exists():
54 print(f'"{configpath}" not found')
55 return 1
56 rundir = configpath.parent
57
58 nodes = []
59 config = json.load(open(configpath, encoding="utf-8"))
60 nodes = list(config.get("topology", {}).get("nodes", []))
61 envcfg = config.get("mucmd", {}).get("env", {})
62
63 # If args.node is not a node it's part of shellcmd
64 if args.node and args.node not in nodes:
65 if args.node != ".":
66 args.shellcmd[0:0] = [args.node]
67 args.node = None
68
69 if args.node:
70 name = args.node
71 nodedir = rundir.joinpath(name)
72 if not nodedir.exists():
73 print('"{name}" node doesn\'t exist in "{rundir}"')
74 return 1
75 rundir = nodedir
76 else:
77 name = "munet"
78 pidpath = rundir.joinpath("nspid")
79 pid = open(pidpath, encoding="ascii").read().strip()
80
81 env = {**os.environ}
82 env["MUNET_NODENAME"] = name
83 env["MUNET_RUNDIR"] = str(rundir)
84
85 for k in envcfg:
86 envcfg[k] = envcfg[k].replace("%NAME%", str(name))
87 envcfg[k] = envcfg[k].replace("%RUNDIR%", str(rundir))
88
89 # Can't use -F if it's a new pid namespace
90 ecmd = "/usr/bin/nsenter"
91 eargs = [ecmd]
92
93 output = subprocess.check_output(["/usr/bin/nsenter", "--help"], encoding="utf-8")
94 if " -a," in output:
95 eargs.append("-a")
96 else:
97 # -U doesn't work
98 for flag in ["-u", "-i", "-m", "-n", "-C", "-T"]:
99 if f" {flag}," in output:
100 eargs.append(flag)
101 eargs.append(f"--pid=/proc/{pid}/ns/pid_for_children")
102 eargs.append(f"--wd={rundir}")
103 eargs.extend(["-t", pid])
104 eargs += args.shellcmd
105 # print("Using ", eargs)
106 return os.execvpe(ecmd, eargs, {**env, **envcfg})
107
108
109 if __name__ == "__main__":
110 exit_status = main()
111 sys.exit(exit_status)