]>
git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/lib/micronet_compat.py
1 # -*- coding: utf-8 eval: (blacken-mode 1) -*-
2 # SPDX-License-Identifier: GPL-2.0-or-later
4 # July 11 2021, Christian Hopps <chopps@labn.net>
6 # Copyright (c) 2021, LabN Consulting, L.L.C
15 from lib
.micronet
import LinuxNamespace
, Micronet
16 from lib
.micronet_cli
import cli
19 def get_pids_with_env(has_var
, has_val
=None):
21 for pidenv
in glob
.iglob("/proc/*/environ"):
22 pid
= pidenv
.split("/")[2]
24 with
open(pidenv
, "rb") as rfb
:
26 x
.decode("utf-8").split("=", 1) for x
in rfb
.read().split(b
"\0")
28 envlist
= [[x
[0], ""] if len(x
) == 1 else x
for x
in envlist
]
29 envdict
= dict(envlist
)
30 if has_var
not in envdict
:
34 elif envdict
[has_var
] == str(has_val
):
37 # E.g., process exited and files are gone
42 def _kill_piddict(pids_by_upid
, sig
):
43 for upid
, pids
in pids_by_upid
:
45 "Sending %s to (%s) of micronet pid %s", sig
, ", ".join(pids
), upid
49 os
.kill(int(pid
), sig
)
55 ourpid
= str(os
.getpid())
56 piddict
= get_pids_with_env("MICRONET_PID", ourpid
)
57 pids
= [x
for x
in piddict
if x
!= ourpid
]
63 def _get_other_pids():
64 piddict
= get_pids_with_env("MICRONET_PID")
65 unet_pids
= {d
["MICRONET_PID"] for d
in piddict
.values()}
66 pids_by_upid
= {p
: set() for p
in unet_pids
}
67 for pid
, envdict
in piddict
.items():
68 pids_by_upid
[envdict
["MICRONET_PID"]].add(pid
)
69 # Filter out any child pid sets whos micronet pid is still running
70 return {x
: y
for x
, y
in pids_by_upid
.items() if x
not in y
}
73 def _get_pids_by_upid(ours
):
75 return _get_our_pids()
76 return _get_other_pids()
79 def _cleanup_pids(ours
):
80 pids_by_upid
= _get_pids_by_upid(ours
).items()
84 _kill_piddict(pids_by_upid
, signal
.SIGTERM
)
86 # Give them 5 second to exit cleanly
87 logging
.info("Waiting up to 5s to allow for clean exit of abandon'd pids")
89 pids_by_upid
= _get_pids_by_upid(ours
).items()
94 pids_by_upid
= _get_pids_by_upid(ours
).items()
95 _kill_piddict(pids_by_upid
, signal
.SIGKILL
)
98 def cleanup_current():
99 """Attempt to cleanup preview runs.
101 Currently this only scans for old processes.
103 logging
.info("reaping current micronet processes")
107 def cleanup_previous():
108 """Attempt to cleanup preview runs.
110 Currently this only scans for old processes.
112 logging
.info("reaping past micronet processes")
116 class Node(LinuxNamespace
):
117 """Node (mininet compat)."""
119 def __init__(self
, name
, **kwargs
):
125 if "private_mounts" in kwargs
:
126 private_mounts
= kwargs
["private_mounts"]
128 private_mounts
= kwargs
.get("privateDirs", [])
130 logger
= kwargs
.get("logger")
132 super(Node
, self
).__init
__(name
, logger
=logger
, private_mounts
=private_mounts
)
134 def cmd(self
, cmd
, **kwargs
):
135 """Execute a command, joins stdout, stderr, ignores exit status."""
137 return super(Node
, self
).cmd_legacy(cmd
, **kwargs
)
139 def config(self
, lo
="up", **params
):
140 """Called by Micronet when topology is built (but not started)."""
141 # mininet brings up loopback here.
152 class Topo(object): # pylint: disable=R0205
153 def __init__(self
, *args
, **kwargs
):
154 raise Exception("Remove Me")
157 class Mininet(Micronet
):
159 Mininet using Micronet.
164 def __init__(self
, controller
=None):
168 assert not controller
170 if Mininet
.g_mnet_inst
is not None:
171 Mininet
.g_mnet_inst
.stop()
172 Mininet
.g_mnet_inst
= self
174 self
.configured_hosts
= set()
175 self
.host_params
= {}
178 # SNMPd used to require this, which was set int he mininet shell
179 # that all commands executed from. This is goofy default so let's not
180 # do it if we don't have to. The snmpd.conf files have been updated
181 # to set permissions to root:frr 770 to make this unneeded in that case
184 super(Mininet
, self
).__init
__()
186 self
.logger
.debug("%s: Creating", self
)
191 def configure_hosts(self
):
193 Configure hosts once the topology has been built.
195 This function can be called multiple times if routers are added to the topology
201 self
.logger
.debug("Configuring hosts: %s", self
.hosts
.keys())
203 for name
in sorted(self
.hosts
.keys()):
204 if name
in self
.configured_hosts
:
207 host
= self
.hosts
[name
]
208 first_intf
= host
.intfs
[0] if host
.intfs
else None
209 params
= self
.host_params
[name
]
211 if first_intf
and "ip" in params
:
215 plen
= self
.prefix_len
217 plen
= int(ip
[i
+ 1 :])
220 host
.cmd_raises("ip addr add {}/{} dev {}".format(ip
, plen
, first_intf
))
222 if "defaultRoute" in params
:
224 "ip route add default {}".format(params
["defaultRoute"])
229 self
.configured_hosts
.add(name
)
231 def add_host(self
, name
, cls
=Node
, **kwargs
):
232 """Add a host to micronet."""
234 self
.host_params
[name
] = kwargs
235 super(Mininet
, self
).add_host(name
, cls
=cls
, **kwargs
)
238 """Start the micronet topology."""
239 self
.logger
.debug("%s: Starting (no-op).", self
)
242 """Stop the mininet topology (deletes)."""
243 self
.logger
.debug("%s: Stopping (deleting).", self
)
247 self
.logger
.debug("%s: Stopped (deleted).", self
)
249 if Mininet
.g_mnet_inst
== self
:
250 Mininet
.g_mnet_inst
= None