Changeset View
Standalone View
tests/atf_python/sys/net/vnet.py
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | class VnetInterface(object): | ||||
IFT_LOOP = 0x18 | IFT_LOOP = 0x18 | ||||
IFT_ETHER = 0x06 | IFT_ETHER = 0x06 | ||||
def __init__(self, iface_alias: str, iface_name: str): | def __init__(self, iface_alias: str, iface_name: str): | ||||
self.name = iface_name | self.name = iface_name | ||||
self.alias = iface_alias | self.alias = iface_alias | ||||
self.vnet_name = "" | self.vnet_name = "" | ||||
self.jailed = False | self.jailed = False | ||||
self.addr_map: Dict[str, Dict] = {"inet6": {}, "inet": {}} | self.addr_map: Dict[str, Dict] = {"inet6": {}, "inet": {}} | ||||
Lint: PEP8 E701: multiple statements on one line (colon) | |||||
self.prefixes4: List[List[str]] = [] | self.prefixes4: List[List[str]] = [] | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
self.prefixes6: List[List[str]] = [] | self.prefixes6: List[List[str]] = [] | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
if iface_name.startswith("lo"): | if iface_name.startswith("lo"): | ||||
self.iftype = self.IFT_LOOP | self.iftype = self.IFT_LOOP | ||||
else: | else: | ||||
self.iftype = self.IFT_ETHER | self.iftype = self.IFT_ETHER | ||||
@property | @property | ||||
def ifindex(self): | def ifindex(self): | ||||
return socket.if_nametoindex(self.name) | return socket.if_nametoindex(self.name) | ||||
Show All 25 Lines | class VnetInterface(object): | ||||
@classmethod | @classmethod | ||||
def setup_loopback(cls, vnet_name: str): | def setup_loopback(cls, vnet_name: str): | ||||
lo = VnetInterface("", "lo0") | lo = VnetInterface("", "lo0") | ||||
lo.set_vnet(vnet_name) | lo.set_vnet(vnet_name) | ||||
lo.turn_up() | lo.turn_up() | ||||
@classmethod | @classmethod | ||||
def create_iface(cls, alias_name: str, iface_name: str) -> List["VnetInterface"]: | def create_iface(cls, alias_name: str, iface_name: str) -> List["VnetInterface"]: | ||||
Lint: PEP8 E501 line too long (85 > 79 characters) Lint: PEP8 E501: line too long (85 > 79 characters) | |||||
name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip() | name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip() | ||||
if not name: | if not name: | ||||
raise Exception("Unable to create iface {}".format(iface_name)) | raise Exception("Unable to create iface {}".format(iface_name)) | ||||
ret = [cls(alias_name, name)] | ret = [cls(alias_name, name)] | ||||
if name.startswith("epair"): | if name.startswith("epair"): | ||||
ret.append(cls(alias_name, name[:-1] + "b")) | ret.append(cls(alias_name, name[:-1] + "b")) | ||||
return ret | return ret | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | class IfaceFactory(object): | ||||
def __init__(self): | def __init__(self): | ||||
self.file_name = self.INTERFACES_FNAME | self.file_name = self.INTERFACES_FNAME | ||||
def _register_iface(self, iface_name: str): | def _register_iface(self, iface_name: str): | ||||
with open(self.file_name, "a") as f: | with open(self.file_name, "a") as f: | ||||
f.write(iface_name + "\n") | f.write(iface_name + "\n") | ||||
def create_iface(self, alias_name: str, iface_name: str) -> List[VnetInterface]: | def create_iface(self, alias_name: str, iface_name: str) -> List[VnetInterface]: | ||||
Lint: PEP8 E501 line too long (84 > 79 characters) Lint: PEP8 E501: line too long (84 > 79 characters) | |||||
ifaces = VnetInterface.create_iface(alias_name, iface_name) | ifaces = VnetInterface.create_iface(alias_name, iface_name) | ||||
for iface in ifaces: | for iface in ifaces: | ||||
self._register_iface(iface.name) | self._register_iface(iface.name) | ||||
return ifaces | return ifaces | ||||
def cleanup(self): | def cleanup(self): | ||||
try: | try: | ||||
with open(self.file_name, "r") as f: | with open(self.file_name, "r") as f: | ||||
for line in f: | for line in f: | ||||
run_cmd("/sbin/ifconfig {} destroy".format(line.strip())) | run_cmd("/sbin/ifconfig {} destroy".format(line.strip())) | ||||
os.unlink(self.INTERFACES_FNAME) | os.unlink(self.INTERFACES_FNAME) | ||||
except Exception: | except Exception: | ||||
pass | pass | ||||
class VnetInstance(object): | class VnetInstance(object): | ||||
def __init__( | def __init__( | ||||
self, vnet_alias: str, vnet_name: str, jid: int, ifaces: List[VnetInterface] | self, vnet_alias: str, vnet_name: str, jid: int, ifaces: List[VnetInterface] | ||||
Lint: PEP8 E501 line too long (84 > 79 characters) Lint: PEP8 E501: line too long (84 > 79 characters) | |||||
): | ): | ||||
self.name = vnet_name | self.name = vnet_name | ||||
self.alias = vnet_alias # reference in the test topology | self.alias = vnet_alias # reference in the test topology | ||||
self.jid = jid | self.jid = jid | ||||
self.ifaces = ifaces | self.ifaces = ifaces | ||||
self.iface_alias_map = {} # iface.alias: iface | self.iface_alias_map = {} # iface.alias: iface | ||||
self.iface_map = {} # iface.name: iface | self.iface_map = {} # iface.name: iface | ||||
for iface in ifaces: | for iface in ifaces: | ||||
Show All 19 Lines | class VnetInstance(object): | ||||
def set_subprocess(self, p): | def set_subprocess(self, p): | ||||
self.subprocess = p | self.subprocess = p | ||||
@staticmethod | @staticmethod | ||||
def attach_jid(jid: int): | def attach_jid(jid: int): | ||||
error_code = libc.jail_attach(jid) | error_code = libc.jail_attach(jid) | ||||
if error_code != 0: | if error_code != 0: | ||||
raise Exception("jail_attach() failed: errno {}".format(error_code)) | raise Exception("jail_attach() failed: errno {}".format(error_code)) | ||||
Lint: PEP8 E501 line too long (80 > 79 characters) Lint: PEP8 E501: line too long (80 > 79 characters) | |||||
def attach(self): | def attach(self): | ||||
self.attach_jid(self.jid) | self.attach_jid(self.jid) | ||||
self.attached = True | self.attached = True | ||||
class VnetFactory(object): | class VnetFactory(object): | ||||
JAILS_FNAME = "created_jails.lst" | JAILS_FNAME = "created_jails.lst" | ||||
def __init__(self, topology_id: str): | def __init__(self, topology_id: str): | ||||
self.topology_id = topology_id | self.topology_id = topology_id | ||||
self.file_name = self.JAILS_FNAME | self.file_name = self.JAILS_FNAME | ||||
self._vnets: List[str] = [] | self._vnets: List[str] = [] | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
def _register_vnet(self, vnet_name: str): | def _register_vnet(self, vnet_name: str): | ||||
self._vnets.append(vnet_name) | self._vnets.append(vnet_name) | ||||
with open(self.file_name, "a") as f: | with open(self.file_name, "a") as f: | ||||
f.write(vnet_name + "\n") | f.write(vnet_name + "\n") | ||||
@staticmethod | @staticmethod | ||||
def _wait_interfaces(vnet_name: str, ifaces: List[str]) -> List[str]: | def _wait_interfaces(vnet_name: str, ifaces: List[str]) -> List[str]: | ||||
cmd = "jexec {} /sbin/ifconfig -l".format(vnet_name) | cmd = "jexec {} /sbin/ifconfig -l".format(vnet_name) | ||||
not_matched: List[str] = [] | not_matched: List[str] = [] | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
for i in range(50): | for i in range(50): | ||||
vnet_ifaces = run_cmd(cmd).strip().split(" ") | vnet_ifaces = run_cmd(cmd).strip().split(" ") | ||||
not_matched = [] | not_matched = [] | ||||
for iface_name in ifaces: | for iface_name in ifaces: | ||||
if iface_name not in vnet_ifaces: | if iface_name not in vnet_ifaces: | ||||
not_matched.append(iface_name) | not_matched.append(iface_name) | ||||
if len(not_matched) == 0: | if len(not_matched) == 0: | ||||
return [] | return [] | ||||
time.sleep(0.1) | time.sleep(0.1) | ||||
return not_matched | return not_matched | ||||
def create_vnet(self, vnet_alias: str, ifaces: List[VnetInterface]): | def create_vnet(self, vnet_alias: str, ifaces: List[VnetInterface]): | ||||
vnet_name = "pytest:{}".format(convert_test_name(self.topology_id)) | vnet_name = "pytest:{}".format(convert_test_name(self.topology_id)) | ||||
if self._vnets: | if self._vnets: | ||||
# add number to distinguish jails | # add number to distinguish jails | ||||
vnet_name = "{}_{}".format(vnet_name, len(self._vnets) + 1) | vnet_name = "{}_{}".format(vnet_name, len(self._vnets) + 1) | ||||
iface_cmds = " ".join(["vnet.interface={}".format(i.name) for i in ifaces]) | iface_cmds = " ".join(["vnet.interface={}".format(i.name) for i in ifaces]) | ||||
Lint: PEP8 E501 line too long (83 > 79 characters) Lint: PEP8 E501: line too long (83 > 79 characters) | |||||
cmd = "/usr/sbin/jail -i -c name={} persist vnet {}".format( | cmd = "/usr/sbin/jail -i -c name={} persist vnet {}".format( | ||||
vnet_name, iface_cmds | vnet_name, iface_cmds | ||||
) | ) | ||||
jid = 0 | jid = 0 | ||||
try: | try: | ||||
jid_str = run_cmd(cmd) | jid_str = run_cmd(cmd) | ||||
jid = int(jid_str) | jid = int(jid_str) | ||||
except ValueError as e: | except ValueError as e: | ||||
print("Jail creation failed, output: {}".format(jid_str)) | print("Jail creation failed, output: {}".format(jid_str)) | ||||
raise | raise | ||||
self._register_vnet(vnet_name) | self._register_vnet(vnet_name) | ||||
# Run expedited version of routing | # Run expedited version of routing | ||||
VnetInterface.setup_loopback(vnet_name) | VnetInterface.setup_loopback(vnet_name) | ||||
not_found = self._wait_interfaces(vnet_name, [i.name for i in ifaces]) | not_found = self._wait_interfaces(vnet_name, [i.name for i in ifaces]) | ||||
if not_found: | if not_found: | ||||
raise Exception( | raise Exception( | ||||
"Interfaces {} has not appeared in vnet {}".format(not_found, vnet_name) | "Interfaces {} has not appeared in vnet {}".format(not_found, vnet_name) | ||||
Lint: PEP8 E501 line too long (88 > 79 characters) Lint: PEP8 E501: line too long (88 > 79 characters) | |||||
) | ) | ||||
return VnetInstance(vnet_alias, vnet_name, jid, ifaces) | return VnetInstance(vnet_alias, vnet_name, jid, ifaces) | ||||
def cleanup(self): | def cleanup(self): | ||||
try: | try: | ||||
with open(self.file_name) as f: | with open(self.file_name) as f: | ||||
for line in f: | for line in f: | ||||
vnet_name = line.strip() | vnet_name = line.strip() | ||||
ToolsHelper.print_output( | ToolsHelper.print_output( | ||||
"/usr/sbin/jexec {} ifconfig -l".format(vnet_name) | "/usr/sbin/jexec {} ifconfig -l".format(vnet_name) | ||||
) | ) | ||||
run_cmd("/usr/sbin/jail -r {}".format(vnet_name)) | run_cmd("/usr/sbin/jail -r {}".format(vnet_name)) | ||||
os.unlink(self.JAILS_FNAME) | os.unlink(self.JAILS_FNAME) | ||||
except OSError: | except OSError: | ||||
pass | pass | ||||
class SingleInterfaceMap(NamedTuple): | class SingleInterfaceMap(NamedTuple): | ||||
ifaces: List[VnetInterface] | ifaces: List[VnetInterface] | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
vnet_aliases: List[str] | vnet_aliases: List[str] | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
class ObjectsMap(NamedTuple): | class ObjectsMap(NamedTuple): | ||||
iface_map: Dict[str, SingleInterfaceMap] # keyed by ifX | iface_map: Dict[str, SingleInterfaceMap] # keyed by ifX | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
vnet_map: Dict[str, VnetInstance] # keyed by vnetX | vnet_map: Dict[str, VnetInstance] # keyed by vnetX | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
topo_map: Dict # self.TOPOLOGY | topo_map: Dict # self.TOPOLOGY | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
class VnetTestTemplate(BaseTest): | class VnetTestTemplate(BaseTest): | ||||
NEED_ROOT: bool = True | |||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
TOPOLOGY = {} | TOPOLOGY = {} | ||||
def _get_vnet_handler(self, vnet_alias: str): | def _get_vnet_handler(self, vnet_alias: str): | ||||
handler_name = "{}_handler".format(vnet_alias) | handler_name = "{}_handler".format(vnet_alias) | ||||
return getattr(self, handler_name, None) | return getattr(self, handler_name, None) | ||||
def _setup_vnet(self, vnet: VnetInstance, obj_map: Dict, pipe): | def _setup_vnet(self, vnet: VnetInstance, obj_map: Dict, pipe): | ||||
"""Base Handler to setup given VNET. | """Base Handler to setup given VNET. | ||||
Show All 29 Lines | def _setup_vnet(self, vnet: VnetInstance, obj_map: Dict, pipe): | ||||
time.sleep(0.1) | time.sleep(0.1) | ||||
# Run actual handler | # Run actual handler | ||||
handler = self._get_vnet_handler(vnet.alias) | handler = self._get_vnet_handler(vnet.alias) | ||||
if handler: | if handler: | ||||
# Do unbuffered stdout for children | # Do unbuffered stdout for children | ||||
# so the logs are present if the child hangs | # so the logs are present if the child hangs | ||||
sys.stdout.reconfigure(line_buffering=True) | sys.stdout.reconfigure(line_buffering=True) | ||||
self.drop_privileges() | |||||
handler(vnet) | handler(vnet) | ||||
def setup_topology(self, topo: Dict, topology_id: str): | def setup_topology(self, topo: Dict, topology_id: str): | ||||
"""Creates jails & interfaces for the provided topology""" | """Creates jails & interfaces for the provided topology""" | ||||
iface_map: Dict[str, SingleInterfaceMap] = {} | iface_map: Dict[str, SingleInterfaceMap] = {} | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
vnet_map = {} | vnet_map = {} | ||||
iface_factory = IfaceFactory() | iface_factory = IfaceFactory() | ||||
vnet_factory = VnetFactory(topology_id) | vnet_factory = VnetFactory(topology_id) | ||||
for obj_name, obj_data in topo.items(): | for obj_name, obj_data in topo.items(): | ||||
if obj_name.startswith("if"): | if obj_name.startswith("if"): | ||||
epair_ifaces = iface_factory.create_iface(obj_name, "epair") | epair_ifaces = iface_factory.create_iface(obj_name, "epair") | ||||
smap = SingleInterfaceMap(epair_ifaces, []) | smap = SingleInterfaceMap(epair_ifaces, []) | ||||
iface_map[obj_name] = smap | iface_map[obj_name] = smap | ||||
Show All 13 Lines | def setup_topology(self, topo: Dict, topology_id: str): | ||||
for vnet_alias, vnet in vnet_map.items(): | for vnet_alias, vnet in vnet_map.items(): | ||||
print("# vnet {} -> {}".format(vnet.alias, vnet.name), end="") | print("# vnet {} -> {}".format(vnet.alias, vnet.name), end="") | ||||
handler = self._get_vnet_handler(vnet.alias) | handler = self._get_vnet_handler(vnet.alias) | ||||
if handler: | if handler: | ||||
print(" handler: {}".format(handler.__name__), end="") | print(" handler: {}".format(handler.__name__), end="") | ||||
print() | print() | ||||
for iface_alias, iface_data in iface_map.items(): | for iface_alias, iface_data in iface_map.items(): | ||||
vnets = iface_data.vnet_aliases | vnets = iface_data.vnet_aliases | ||||
ifaces: List[VnetInterface] = iface_data.ifaces | ifaces: List[VnetInterface] = iface_data.ifaces | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
if len(vnets) == 1 and len(ifaces) == 2: | if len(vnets) == 1 and len(ifaces) == 2: | ||||
print( | print( | ||||
"# iface {}: {}::{} -> main::{}".format( | "# iface {}: {}::{} -> main::{}".format( | ||||
iface_alias, vnets[0], ifaces[0].name, ifaces[1].name | iface_alias, vnets[0], ifaces[0].name, ifaces[1].name | ||||
) | ) | ||||
) | ) | ||||
elif len(vnets) == 2 and len(ifaces) == 2: | elif len(vnets) == 2 and len(ifaces) == 2: | ||||
print( | print( | ||||
"# iface {}: {}::{} -> {}::{}".format( | "# iface {}: {}::{} -> {}::{}".format( | ||||
iface_alias, vnets[0], ifaces[0].name, vnets[1], ifaces[1].name | iface_alias, vnets[0], ifaces[0].name, vnets[1], ifaces[1].name | ||||
Lint: PEP8 E501 line too long (87 > 79 characters) Lint: PEP8 E501: line too long (87 > 79 characters) | |||||
) | ) | ||||
) | ) | ||||
else: | else: | ||||
print( | print( | ||||
"# iface {}: ifaces: {} vnets: {}".format( | "# iface {}: ifaces: {} vnets: {}".format( | ||||
iface_alias, vnets, [i.name for i in ifaces] | iface_alias, vnets, [i.name for i in ifaces] | ||||
) | ) | ||||
) | ) | ||||
Show All 30 Lines | def setup_method(self, _method): | ||||
main_vnet = vnet | main_vnet = vnet | ||||
# Main vnet needs to be the last, so all the other subprocesses | # Main vnet needs to be the last, so all the other subprocesses | ||||
# are started & their pipe handles collected | # are started & their pipe handles collected | ||||
self.vnet = main_vnet | self.vnet = main_vnet | ||||
self._setup_vnet(main_vnet, obj_map, None) | self._setup_vnet(main_vnet, obj_map, None) | ||||
# Save state for the main handler | # Save state for the main handler | ||||
self.iface_map = obj_map.iface_map | self.iface_map = obj_map.iface_map | ||||
self.vnet_map = obj_map.vnet_map | self.vnet_map = obj_map.vnet_map | ||||
self.drop_privileges() | |||||
def cleanup(self, test_id: str): | def cleanup(self, test_id: str): | ||||
# pytest test id: file::class::test_name | # pytest test id: file::class::test_name | ||||
topology_id = get_topology_id(self.test_id) | topology_id = get_topology_id(self.test_id) | ||||
print("==== vnet cleanup ===") | print("==== vnet cleanup ===") | ||||
print("# topology_id: '{}'".format(topology_id)) | print("# topology_id: '{}'".format(topology_id)) | ||||
VnetFactory(topology_id).cleanup() | VnetFactory(topology_id).cleanup() | ||||
IfaceFactory().cleanup() | IfaceFactory().cleanup() | ||||
def wait_object(self, pipe, timeout=5): | def wait_object(self, pipe, timeout=5): | ||||
if pipe.poll(timeout): | if pipe.poll(timeout): | ||||
return pipe.recv() | return pipe.recv() | ||||
raise TimeoutError | raise TimeoutError | ||||
def send_object(self, pipe, obj): | def send_object(self, pipe, obj): | ||||
pipe.send(obj) | pipe.send(obj) | ||||
@property | @property | ||||
def curvnet(self): | def curvnet(self): | ||||
pass | pass | ||||
class SingleVnetTestTemplate(VnetTestTemplate): | class SingleVnetTestTemplate(VnetTestTemplate): | ||||
IPV6_PREFIXES: List[str] = [] | IPV6_PREFIXES: List[str] = [] | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
IPV4_PREFIXES: List[str] = [] | IPV4_PREFIXES: List[str] = [] | ||||
Lint: PEP8 E701 multiple statements on one line (colon) Lint: PEP8 E701: multiple statements on one line (colon) | |||||
def setup_method(self, method): | def setup_method(self, method): | ||||
topology = copy.deepcopy( | topology = copy.deepcopy( | ||||
{ | { | ||||
"vnet1": {"ifaces": ["if1"]}, | "vnet1": {"ifaces": ["if1"]}, | ||||
"if1": {"prefixes4": [], "prefixes6": []}, | "if1": {"prefixes4": [], "prefixes6": []}, | ||||
} | } | ||||
) | ) | ||||
for prefix in self.IPV6_PREFIXES: | for prefix in self.IPV6_PREFIXES: | ||||
topology["if1"]["prefixes6"].append((prefix,)) | topology["if1"]["prefixes6"].append((prefix,)) | ||||
for prefix in self.IPV4_PREFIXES: | for prefix in self.IPV4_PREFIXES: | ||||
topology["if1"]["prefixes4"].append((prefix,)) | topology["if1"]["prefixes4"].append((prefix,)) | ||||
self.TOPOLOGY = topology | self.TOPOLOGY = topology | ||||
super().setup_method(method) | super().setup_method(method) |
multiple statements on one line (colon)