aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander V. Chernikov <melifaro@FreeBSD.org>2022-12-29 19:07:34 +0000
committerAlexander V. Chernikov <melifaro@FreeBSD.org>2022-12-29 19:59:11 +0000
commitf63825ff21a3bee2630ea8b0ed27a4583cc4242b (patch)
tree39722fc957e83ca73dfc200463def1fe4b772092
parent7063b9974f8a39d860b7abd03884324e71994f65 (diff)
downloadsrc-f63825ff21a3bee2630ea8b0ed27a4583cc4242b.tar.gz
src-f63825ff21a3bee2630ea8b0ed27a4583cc4242b.zip
testing: improve python vnet wrapper.
* Derive jail name from class name and method name, instead of just method name. This change reduces the chances of different tests clashing. Old: 'jail_test_one'. New: 'pytest:TestExampleSimplest:test_one' * Simplify vnetX_handler() method signature by skipping obj_map (unused) and pipe. The latter can be accessed as the vnet property. * Add `send_object()` method as a pair to the `wait_object` inside the VnetTestTemplate class. * Add `test_id` property to the BaseTest method. Previously it was provided only for the VnetTestTemplate class. This change makes the identifier easily accessible for all users. MFC after: 2 weeks
-rw-r--r--tests/atf_python/sys/net/vnet.py92
-rw-r--r--tests/atf_python/utils.py12
-rw-r--r--tests/sys/netinet6/test_ip6_output.py30
3 files changed, 82 insertions, 52 deletions
diff --git a/tests/atf_python/sys/net/vnet.py b/tests/atf_python/sys/net/vnet.py
index faae58e95b6f..aca1b53d388c 100644
--- a/tests/atf_python/sys/net/vnet.py
+++ b/tests/atf_python/sys/net/vnet.py
@@ -12,7 +12,8 @@ from typing import List
from typing import NamedTuple
from atf_python.sys.net.tools import ToolsHelper
-from atf_python.utils import libc, BaseTest
+from atf_python.utils import BaseTest
+from atf_python.utils import libc
def run_cmd(cmd: str, verbose=True) -> str:
@@ -20,11 +21,20 @@ def run_cmd(cmd: str, verbose=True) -> str:
return os.popen(cmd).read()
+def get_topology_id(test_id: str) -> str:
+ """
+ Gets a unique topology id based on the pytest test_id.
+ "test_ip6_output.py::TestIP6Output::test_output6_pktinfo[ipandif]" ->
+ "TestIP6Output:test_output6_pktinfo[ipandif]"
+ """
+ return ":".join(test_id.split("::")[-2:])
+
+
def convert_test_name(test_name: str) -> str:
"""Convert test name to a string that can be used in the file/jail names"""
ret = ""
for char in test_name:
- if char.isalnum() or char in ("_", "-"):
+ if char.isalnum() or char in ("_", "-", ":"):
ret += char
elif char in ("["):
ret += "_"
@@ -140,9 +150,7 @@ class VnetInterface(object):
class IfaceFactory(object):
INTERFACES_FNAME = "created_ifaces.lst"
- def __init__(self, test_name: str):
- self.test_name = test_name
- self.test_id = convert_test_name(test_name)
+ def __init__(self):
self.file_name = self.INTERFACES_FNAME
def _register_iface(self, iface_name: str):
@@ -213,9 +221,8 @@ class VnetInstance(object):
class VnetFactory(object):
JAILS_FNAME = "created_jails.lst"
- def __init__(self, test_name: str):
- self.test_name = test_name
- self.test_id = convert_test_name(test_name)
+ def __init__(self, topology_id: str):
+ self.topology_id = topology_id
self.file_name = self.JAILS_FNAME
self._vnets: List[str] = []
@@ -240,7 +247,7 @@ class VnetFactory(object):
return not_matched
def create_vnet(self, vnet_alias: str, ifaces: List[VnetInterface]):
- vnet_name = "jail_{}".format(self.test_id)
+ vnet_name = "pytest:{}".format(convert_test_name(self.topology_id))
if self._vnets:
# add number to distinguish jails
vnet_name = "{}_{}".format(vnet_name, len(self._vnets) + 1)
@@ -248,10 +255,13 @@ class VnetFactory(object):
cmd = "/usr/sbin/jail -i -c name={} persist vnet {}".format(
vnet_name, iface_cmds
)
- jid_str = run_cmd(cmd)
- jid = int(jid_str)
- if jid <= 0:
- raise Exception("Jail creation failed, output: {}".format(jid))
+ jid = 0
+ try:
+ jid_str = run_cmd(cmd)
+ jid = int(jid_str)
+ except ValueError as e:
+ print("Jail creation failed, output: {}".format(jid_str))
+ raise
self._register_vnet(vnet_name)
# Run expedited version of routing
@@ -268,11 +278,11 @@ class VnetFactory(object):
try:
with open(self.file_name) as f:
for line in f:
- jail_name = line.strip()
+ vnet_name = line.strip()
ToolsHelper.print_output(
- "/usr/sbin/jexec {} ifconfig -l".format(jail_name)
+ "/usr/sbin/jexec {} ifconfig -l".format(vnet_name)
)
- run_cmd("/usr/sbin/jail -r {}".format(line.strip()))
+ run_cmd("/usr/sbin/jail -r {}".format(vnet_name))
os.unlink(self.JAILS_FNAME)
except OSError:
pass
@@ -283,6 +293,12 @@ class SingleInterfaceMap(NamedTuple):
vnet_aliases: List[str]
+class ObjectsMap(NamedTuple):
+ iface_map: Dict[str, SingleInterfaceMap] # keyed by ifX
+ vnet_map: Dict[str, VnetInstance] # keyed by vnetX
+ topo_map: Dict # self.TOPOLOGY
+
+
class VnetTestTemplate(BaseTest):
TOPOLOGY = {}
@@ -297,8 +313,10 @@ class VnetTestTemplate(BaseTest):
"""
vnet.attach()
print("# setup_vnet({})".format(vnet.name))
+ if pipe is not None:
+ vnet.set_pipe(pipe)
- topo = obj_map["topo_map"]
+ topo = obj_map.topo_map
ipv6_ifaces = []
# Disable DAD
if not vnet.need_dad:
@@ -306,7 +324,7 @@ class VnetTestTemplate(BaseTest):
for iface in vnet.ifaces:
# check index of vnet within an interface
# as we have prefixes for both ends of the interface
- iface_map = obj_map["iface_map"][iface.alias]
+ iface_map = obj_map.iface_map[iface.alias]
idx = iface_map.vnet_aliases.index(vnet.alias)
prefixes6 = topo[iface.alias].get("prefixes6", [])
prefixes4 = topo[iface.alias].get("prefixes4", [])
@@ -327,14 +345,14 @@ class VnetTestTemplate(BaseTest):
# Do unbuffered stdout for children
# so the logs are present if the child hangs
sys.stdout.reconfigure(line_buffering=True)
- handler(vnet, obj_map, pipe)
+ handler(vnet)
- def setup_topology(self, topo: Dict, test_name: str):
+ def setup_topology(self, topo: Dict, topology_id: str):
"""Creates jails & interfaces for the provided topology"""
iface_map: Dict[str, SingleInterfaceMap] = {}
vnet_map = {}
- iface_factory = IfaceFactory(test_name)
- vnet_factory = VnetFactory(test_name)
+ iface_factory = IfaceFactory()
+ vnet_factory = VnetFactory(topology_id)
for obj_name, obj_data in topo.items():
if obj_name.startswith("if"):
epair_ifaces = iface_factory.create_iface(obj_name, "epair")
@@ -381,19 +399,18 @@ class VnetTestTemplate(BaseTest):
)
)
print()
- return {"iface_map": iface_map, "vnet_map": vnet_map, "topo_map": topo}
+ return ObjectsMap(iface_map, vnet_map, topo)
- def setup_method(self, method):
+ def setup_method(self, _method):
"""Sets up all the required topology and handlers for the given test"""
- # 'test_ip6_output.py::TestIP6Output::test_output6_pktinfo[ipandif] (setup)'
- test_id = os.environ.get("PYTEST_CURRENT_TEST").split(" ")[0]
- test_name = test_id.split("::")[-1]
- self.check_constraints()
+ super().setup_method(_method)
+ # TestIP6Output.test_output6_pktinfo[ipandif]
+ topology_id = get_topology_id(self.test_id)
topology = self.TOPOLOGY
# First, setup kernel objects - interfaces & vnets
- obj_map = self.setup_topology(topology, test_name)
+ obj_map = self.setup_topology(topology, topology_id)
main_vnet = None # one without subprocess handler
- for vnet_alias, vnet in obj_map["vnet_map"].items():
+ for vnet_alias, vnet in obj_map.vnet_map.items():
if self._get_vnet_handler(vnet_alias):
# Need subprocess to run
parent_pipe, child_pipe = Pipe()
@@ -417,23 +434,26 @@ class VnetTestTemplate(BaseTest):
self.vnet = main_vnet
self._setup_vnet(main_vnet, obj_map, None)
# Save state for the main handler
- self.iface_map = obj_map["iface_map"]
- self.vnet_map = obj_map["vnet_map"]
+ self.iface_map = obj_map.iface_map
+ self.vnet_map = obj_map.vnet_map
def cleanup(self, test_id: str):
# pytest test id: file::class::test_name
- test_name = test_id.split("::")[-1]
+ topology_id = get_topology_id(self.test_id)
print("==== vnet cleanup ===")
- print("# test_name: '{}'".format(test_name))
- VnetFactory(test_name).cleanup()
- IfaceFactory(test_name).cleanup()
+ print("# topology_id: '{}'".format(topology_id))
+ VnetFactory(topology_id).cleanup()
+ IfaceFactory().cleanup()
def wait_object(self, pipe, timeout=5):
if pipe.poll(timeout):
return pipe.recv()
raise TimeoutError
+ def send_object(self, pipe, obj):
+ pipe.send(obj)
+
@property
def curvnet(self):
pass
diff --git a/tests/atf_python/utils.py b/tests/atf_python/utils.py
index 12cd56c10149..17824262b1fd 100644
--- a/tests/atf_python/utils.py
+++ b/tests/atf_python/utils.py
@@ -42,5 +42,15 @@ class BaseTest(object):
"kernel module '{}' not available: {}".format(mod_name, err_str)
)
- def check_constraints(self):
+ @property
+ def test_id(self):
+ # 'test_ip6_output.py::TestIP6Output::test_output6_pktinfo[ipandif] (setup)'
+ return os.environ.get("PYTEST_CURRENT_TEST").split(" ")[0]
+
+ def setup_method(self, method):
+ """Run all pre-requisits for the test execution"""
self._check_modules()
+
+ def cleanup(self, test_id: str):
+ """Cleanup all test resources here"""
+ pass
diff --git a/tests/sys/netinet6/test_ip6_output.py b/tests/sys/netinet6/test_ip6_output.py
index 35adb6a7137a..fc821606a726 100644
--- a/tests/sys/netinet6/test_ip6_output.py
+++ b/tests/sys/netinet6/test_ip6_output.py
@@ -73,24 +73,24 @@ class BaseTestIP6Ouput(VnetTestTemplate):
}
DEFAULT_PORT = 45365
- def _vnet2_handler(self, vnet, obj_map, pipe, ip: str, os_ifname: str = None):
+ def _vnet2_handler(self, vnet, ip: str, os_ifname: str = None):
"""Generic listener that sends first received packet with metadata
back to the sender via pipw
"""
ll_data = ToolsHelper.get_linklocals()
# Start listener
ss = VerboseSocketServer(ip, self.DEFAULT_PORT, os_ifname)
- pipe.send(ll_data)
+ vnet.pipe.send(ll_data)
tx_obj = ss.recv()
tx_obj["dst_iface_alias"] = vnet.iface_map[tx_obj["dst_iface"]].alias
- pipe.send(tx_obj)
+ vnet.pipe.send(tx_obj)
class TestIP6Output(BaseTestIP6Ouput):
- def vnet2_handler(self, vnet, obj_map, pipe):
+ def vnet2_handler(self, vnet):
ip = str(vnet.iface_alias_map["if2"].first_ipv6.ip)
- self._vnet2_handler(vnet, obj_map, pipe, ip, None)
+ self._vnet2_handler(vnet, ip, None)
@pytest.mark.require_user("root")
def test_output6_base(self):
@@ -221,14 +221,14 @@ class TestIP6Output(BaseTestIP6Ouput):
class TestIP6OutputLL(BaseTestIP6Ouput):
- def vnet2_handler(self, vnet, obj_map, pipe):
+ def vnet2_handler(self, vnet):
"""Generic listener that sends first received packet with metadata
back to the sender via pipw
"""
os_ifname = vnet.iface_alias_map["if2"].name
ll_data = ToolsHelper.get_linklocals()
ll_ip, _ = ll_data[os_ifname][0]
- self._vnet2_handler(vnet, obj_map, pipe, ll_ip, os_ifname)
+ self._vnet2_handler(vnet, ll_ip, os_ifname)
@pytest.mark.require_user("root")
def test_output6_linklocal(self):
@@ -258,12 +258,12 @@ class TestIP6OutputLL(BaseTestIP6Ouput):
class TestIP6OutputNhopLL(BaseTestIP6Ouput):
- def vnet2_handler(self, vnet, obj_map, pipe):
+ def vnet2_handler(self, vnet):
"""Generic listener that sends first received packet with metadata
back to the sender via pipw
"""
ip = str(vnet.iface_alias_map["if2"].first_ipv6.ip)
- self._vnet2_handler(vnet, obj_map, pipe, ip, None)
+ self._vnet2_handler(vnet, ip, None)
@pytest.mark.require_user("root")
def test_output6_nhop_linklocal(self):
@@ -296,11 +296,11 @@ class TestIP6OutputNhopLL(BaseTestIP6Ouput):
class TestIP6OutputScope(BaseTestIP6Ouput):
- def vnet2_handler(self, vnet, obj_map, pipe):
+ def vnet2_handler(self, vnet):
"""Generic listener that sends first received packet with metadata
back to the sender via pipw
"""
- bind_ip, bind_ifp = self.wait_object(pipe)
+ bind_ip, bind_ifp = self.wait_object(vnet.pipe)
if bind_ip is None:
os_ifname = vnet.iface_alias_map[bind_ifp].name
ll_data = ToolsHelper.get_linklocals()
@@ -308,7 +308,7 @@ class TestIP6OutputScope(BaseTestIP6Ouput):
if bind_ifp is not None:
bind_ifp = vnet.iface_alias_map[bind_ifp].name
print("## BIND {}%{}".format(bind_ip, bind_ifp))
- self._vnet2_handler(vnet, obj_map, pipe, bind_ip, bind_ifp)
+ self._vnet2_handler(vnet, bind_ip, bind_ifp)
@pytest.mark.parametrize(
"params",
@@ -402,10 +402,10 @@ class TestIP6OutputScope(BaseTestIP6Ouput):
class TestIP6OutputMulticast(BaseTestIP6Ouput):
- def vnet2_handler(self, vnet, obj_map, pipe):
- group = self.wait_object(pipe)
+ def vnet2_handler(self, vnet):
+ group = self.wait_object(vnet.pipe)
os_ifname = vnet.iface_alias_map["if2"].name
- self._vnet2_handler(vnet, obj_map, pipe, group, os_ifname)
+ self._vnet2_handler(vnet, group, os_ifname)
@pytest.mark.parametrize("group_scope", ["ff02", "ff05", "ff08", "ff0e"])
@pytest.mark.require_user("root")