1
0
Fork 0
frr/tests/topotests/munet/mucmd.py
Daniel Baumann 3124f89aed
Adding upstream version 10.1.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-05 10:03:58 +01:00

105 lines
3.1 KiB
Python

# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
# December 5 2021, Christian Hopps <chopps@labn.net>
#
# Copyright 2021, LabN Consulting, L.L.C.
#
"""A command that allows external command execution inside nodes."""
import argparse
import json
import os
import sys
from pathlib import Path
def newest_file_in(filename, paths, has_sibling=None):
new = None
newst = None
items = (x for y in paths for x in Path(y).rglob(filename))
for e in items:
st = os.stat(e)
if has_sibling and not e.parent.joinpath(has_sibling).exists():
continue
if not new or st.st_mtime_ns > newst.st_mtime_ns:
new = e
newst = st
continue
return new, newst
def main(*args):
ap = argparse.ArgumentParser(args)
ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc")
ap.add_argument("node", nargs="?", help="node to enter or run command inside")
ap.add_argument(
"shellcmd",
nargs=argparse.REMAINDER,
help="optional shell-command to execute on NODE",
)
args = ap.parse_args()
if args.rundir:
configpath = Path(args.rundir).joinpath("config.json")
else:
configpath, _ = newest_file_in(
"config.json",
["/tmp/munet", "/tmp/mutest", "/tmp/unet-test"],
has_sibling=args.node,
)
print(f'Using "{configpath}"')
if not configpath.exists():
print(f'"{configpath}" not found')
return 1
rundir = configpath.parent
nodes = []
config = json.load(open(configpath, encoding="utf-8"))
nodes = list(config.get("topology", {}).get("nodes", []))
envcfg = config.get("mucmd", {}).get("env", {})
# If args.node is not a node it's part of shellcmd
if args.node and args.node not in nodes:
if args.node != ".":
args.shellcmd[0:0] = [args.node]
args.node = None
if args.node:
name = args.node
nodedir = rundir.joinpath(name)
if not nodedir.exists():
print('"{name}" node doesn\'t exist in "{rundir}"')
return 1
rundir = nodedir
else:
name = "munet"
pidpath = rundir.joinpath("nspid")
pid = open(pidpath, encoding="ascii").read().strip()
env = {**os.environ}
env["MUNET_NODENAME"] = name
env["MUNET_RUNDIR"] = str(rundir)
for k in envcfg:
envcfg[k] = envcfg[k].replace("%NAME%", str(name))
envcfg[k] = envcfg[k].replace("%RUNDIR%", str(rundir))
# Can't use -F if it's a new pid namespace
ecmd = "/usr/bin/nsenter"
eargs = [ecmd]
#start mucmd same way base process is started
eargs.append(f"--mount=/proc/{pid}/ns/mnt")
eargs.append(f"--net=/proc/{pid}/ns/net")
eargs.append(f"--pid=/proc/{pid}/ns/pid_for_children")
eargs.append(f"--uts=/proc/{pid}/ns/uts")
eargs.append(f"--wd={rundir}")
eargs += args.shellcmd
#print("Using ", eargs)
return os.execvpe(ecmd, eargs, {**env, **envcfg})
if __name__ == "__main__":
exit_status = main()
sys.exit(exit_status)