Skip to content

Commit

Permalink
munet: 0.14.8: dont kill other munet procs by default
Browse files Browse the repository at this point in the history
- add `--kill` option to cleanup existing processes using same
  rundir.
- detect if something is running using the same rundir and exit
  mentioning `--kill` option to cleanup

Signed-off-by: Christian Hopps <chopps@labn.net>
  • Loading branch information
choppsv1 committed May 22, 2024
1 parent 3057893 commit 8495a72
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 34 deletions.
24 changes: 19 additions & 5 deletions munet/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .args import add_launch_args
from .base import get_event_loop
from .cleanup import cleanup_previous
from .cleanup import is_running_in_rundir
from .compat import PytestConfig


Expand Down Expand Up @@ -139,10 +140,11 @@ def main(*args):
eap = ap.add_argument_group(title="Uncommon", description="uncommonly used options")
eap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)")
eap.add_argument(
"--no-kill",
"--kill",
action="store_true",
help="Do not kill previous running processes",
help="Kill previous running processes using same rundir and exit",
)
eap.add_argument("--no-kill", action="store_true", help=argparse.SUPPRESS)
eap.add_argument(
"--no-cli", action="store_true", help="Do not run the interactive CLI"
)
Expand All @@ -157,7 +159,18 @@ def main(*args):
sys.exit(0)

rundir = args.rundir if args.rundir else "/tmp/munet"
rundir = os.path.abspath(rundir)
args.rundir = rundir

if args.kill:
logging.info("Killing any previous run using rundir: {rundir}")
cleanup_previous(args.rundir)
elif is_running_in_rundir(args.rundir):
logging.fatal(
"Munet processes using rundir: %s, use `--kill` to cleanup first", rundir
)
return 1

if args.cleanup:
if os.path.exists(rundir):
if not os.path.exists(f"{rundir}/config.json"):
Expand All @@ -169,6 +182,10 @@ def main(*args):
sys.exit(1)
else:
subprocess.run(["/usr/bin/rm", "-rf", rundir], check=True)

if args.kill:
return 0

subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True)
os.environ["MUNET_RUNDIR"] = rundir

Expand All @@ -183,9 +200,6 @@ def main(*args):
logger.critical("No nodes defined in config file")
return 1

if not args.no_kill:
cleanup_previous()

loop = None
status = 4
try:
Expand Down
20 changes: 15 additions & 5 deletions munet/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,8 @@ def _get_sub_args(self, cmd_list, defaults, use_pty=False, ns_only=False, **kwar
env = {**(kwargs["env"] if "env" in kwargs else os.environ)}
if "MUNET_NODENAME" not in env:
env["MUNET_NODENAME"] = self.name
if "MUNET_PID" not in env and "MUNET_PID" in os.environ:
env["MUNET_PID"] = os.environ["MUNET_PID"]
kwargs["env"] = env

defaults.update(kwargs)
Expand Down Expand Up @@ -1226,7 +1228,13 @@ def run_in_window(
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
cmd = [
"/usr/bin/env",
f"MUNET_NODENAME={self.name}",
]
if "MUNET_PID" in os.environ:
cmd.append(f"MUNET_PID={os.environ.get('MUNET_PID')}")
cmd += cmd

# get the ssh cmd
cmd = self._get_pre_cmd(False, True, ns_only=ns_only) + [shlex.join(cmd)]
Expand All @@ -1246,6 +1254,8 @@ def run_in_window(
envvars = f"MUNET_NODENAME={self.name} NODENAME={self.name}"
if hasattr(self, "rundir"):
envvars += f" RUNDIR={self.rundir}"
if "MUNET_PID" in os.environ:
envvars += f" MUNET_PID={os.environ.get('MUNET_PID')}"
if hasattr(self.unet, "config_dirname") and self.unet.config_dirname:
envvars += f" CONFIGDIR={self.unet.config_dirname}"
elif "CONFIGDIR" in os.environ:
Expand Down Expand Up @@ -2650,10 +2660,6 @@ def __init__(

self.cfgopt = munet_config.ConfigOptionsProxy(pytestconfig)

super().__init__(
name, mount=True, net=isolated, uts=isolated, pid=pid, unet=None, **kwargs
)

# This allows us to cleanup any leftover running munet's
if "MUNET_PID" in os.environ:
if os.environ["MUNET_PID"] != str(our_pid):
Expand All @@ -2664,6 +2670,10 @@ def __init__(
)
os.environ["MUNET_PID"] = str(our_pid)

super().__init__(
name, mount=True, net=isolated, uts=isolated, pid=pid, unet=None, **kwargs
)

# this is for testing purposes do not use
if not BaseMunet.g_unet:
BaseMunet.g_unet = self
Expand Down
34 changes: 23 additions & 11 deletions munet/cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,33 @@ def _get_our_pids():
return {}


def _get_other_pids():
piddict = get_pids_with_env("MUNET_PID")
unet_pids = {d["MUNET_PID"] for d in piddict.values()}
def _get_other_pids(rundir):
if rundir:
# get only munet pids using the given rundir
piddict = get_pids_with_env("MUNET_RUNDIR", str(rundir))
else:
# Get all munet pids
piddict = get_pids_with_env("MUNET_PID")
unet_pids = {d["MUNET_PID"] for d in piddict.values() if "MUNET_PID" in d}
pids_by_upid = {p: set() for p in unet_pids}
for pid, envdict in piddict.items():
if "MUNET_PID" not in envdict:
continue
unet_pid = envdict["MUNET_PID"]
pids_by_upid[unet_pid].add(pid)
# Filter out any child pid sets whos munet pid is still running
return {x: y for x, y in pids_by_upid.items() if x not in y}


def _get_pids_by_upid(ours):
def _get_pids_by_upid(ours, rundir):
if ours:
assert rundir is None
return _get_our_pids()
return _get_other_pids()
return _get_other_pids(rundir)


def _cleanup_pids(ours):
pids_by_upid = _get_pids_by_upid(ours).items()
def _cleanup_pids(ours, rundir):
pids_by_upid = _get_pids_by_upid(ours, rundir).items()
if not pids_by_upid:
return

Expand All @@ -94,7 +102,7 @@ def _cleanup_pids(ours):
# return
# time.sleep(1)

pids_by_upid = _get_pids_by_upid(ours).items()
pids_by_upid = _get_pids_by_upid(ours, rundir).items()
_kill_piddict(pids_by_upid, signal.SIGKILL)


Expand All @@ -103,12 +111,16 @@ def cleanup_current():
Currently this only scans for old processes.
"""
_cleanup_pids(True)
_cleanup_pids(True, None)


def cleanup_previous():
def cleanup_previous(rundir=None):
"""Attempt to cleanup preview runs.
Currently this only scans for old processes.
"""
_cleanup_pids(False)
_cleanup_pids(False, rundir)


def is_running_in_rundir(rundir):
return bool(get_pids_with_env("MUNET_RUNDIR", str(rundir)))
5 changes: 3 additions & 2 deletions munet/mutest/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,9 @@ def main():
sys.exit(0)

rundir = args.rundir if args.rundir else "/tmp/mutest"
args.rundir = Path(rundir)
os.environ["MUNET_RUNDIR"] = rundir
rundir = Path(rundir).absolute()
args.rundir = rundir
os.environ["MUNET_RUNDIR"] = str(rundir)
subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True)

config = parser.setup_logging(args, config_base="logconf-mutest")
Expand Down
24 changes: 14 additions & 10 deletions munet/testing/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from ..base import Bridge
from ..base import get_event_loop
from ..cleanup import cleanup_current
from ..cleanup import cleanup_previous
from ..native import L3NodeMixin
from ..parser import async_build_topology
from ..parser import get_config
Expand Down Expand Up @@ -130,9 +129,12 @@ def session_autouse():
else:
is_worker = True

if not is_worker:
# This is unfriendly to multi-instance
cleanup_previous()
# We dont want to kill all munet and we don't have the rundir here yet
# This was more useful back when we used to leave processes around a lot
# more.
# if not is_worker:
# # This is unfriendly to multi-instance
# cleanup_previous()

# We never pop as we want to keep logging
_push_log_handler("session", "/tmp/unet-test/pytest-session.log")
Expand All @@ -150,8 +152,9 @@ def session_autouse():

@pytest.fixture(autouse=True, scope="module")
def module_autouse(request):
root_path = os.environ.get("MUNET_RUNDIR", "/tmp/unet-test")
logpath = get_test_logdir(request.node.nodeid, True)
logpath = os.path.join("/tmp/unet-test", logpath, "pytest-exec.log")
logpath = os.path.join(root_path, logpath, "pytest-exec.log")
with log_handler("module", logpath):
sdir = os.path.dirname(os.path.realpath(request.fspath))
with chdir(sdir, "module autouse fixture"):
Expand All @@ -174,7 +177,8 @@ def event_loop():

@pytest.fixture(scope="module")
def rundir_module():
d = os.path.join("/tmp/unet-test", get_test_logdir(module=True))
root_path = os.environ.get("MUNET_RUNDIR", "/tmp/unet-test")
d = os.path.join(root_path, get_test_logdir(module=True))
logging.debug("conftest: test module rundir %s", d)
return d

Expand Down Expand Up @@ -375,17 +379,17 @@ async def stepfunction(desc=""):

@pytest.fixture(scope="function")
def rundir():
d = os.path.join("/tmp/unet-test", get_test_logdir(module=False))
root_path = os.environ.get("MUNET_RUNDIR", "/tmp/unet-test")
d = os.path.join(root_path, get_test_logdir(module=False))
logging.debug("conftest: test function rundir %s", d)
return d


# Configure logging
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_setup(item):
d = os.path.join(
"/tmp/unet-test", get_test_logdir(nodeid=item.nodeid, module=False)
)
root_path = os.environ.get("MUNET_RUNDIR", "/tmp/unet-test")
d = os.path.join(root_path, get_test_logdir(nodeid=item.nodeid, module=False))
config = item.config
logging_plugin = config.pluginmanager.get_plugin("logging-plugin")
filename = Path(d, "pytest-exec.log")
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "munet"
version = "0.14.7"
version = "0.14.8"
description = "A package to facilitate network simulations"
authors = ["Christian Hopps <chopps@labn.net>"]
license = "GPL-2.0-or-later"
Expand Down

0 comments on commit 8495a72

Please sign in to comment.