tests: refactor virtual interface tests
[vpp.git] / test / vm_vpp_interfaces.py
similarity index 80%
rename from test/test_vm_vpp_interfaces.py
rename to test/vm_vpp_interfaces.py
index 0c90325..ed2d55b 100644 (file)
@@ -37,12 +37,29 @@ from vm_test_config import test_config
 #
 
 
-def filter_tests(test):
-    """Filter test IDs to include only those selected to run."""
-    selection = test_config["tests_to_run"]
-    if not selection or selection == " ":
-        return True
-    else:
+class TestSelector:
+    """Selects specified test(s) from vm_test_config to run
+
+    The selected_test field specifies a comma separated or range(s) of
+    tests to run (default='' i.e all_tests) e.g. setting the selected_tests
+    attribute to "1,3-4,19-23" runs tests with ID's 1, 3, 4, 19, 20, 21,
+    22 & 23 from the spec file vm_test_config
+    """
+
+    def __init__(self, selected_tests="") -> None:
+        self.selected_tests = selected_tests
+
+    def filter_tests(self, test):
+        """Works with the filter fn. to include only selected tests."""
+
+        if self.selected_tests:
+            selection = self.selected_tests
+        else:
+            selection = test_config["tests_to_run"]
+
+        if not selection or selection == " ":
+            return True
+
         test_ids_to_run = []
         for test_id in selection.split(","):
             if "-" in test_id.strip():
@@ -54,9 +71,6 @@ def filter_tests(test):
 
 
 # Test Config variables
-client_namespace = test_config["client_namespace"]
-server_namespace = test_config["server_namespace"]
-tests = filter(filter_tests, test_config["tests"])
 af_packet_config = test_config["af_packet"]
 layer2 = test_config["L2"]
 layer3 = test_config["L3"]
@@ -86,12 +100,26 @@ def create_test(test_name, test, ip_version, mtu):
                 memif_sock_path=self.get_memif_sock_path(), logger=self.logger
             )
         if result is True:
-            start_iperf(ip_version=6, server_only=True, logger=self.logger)
+            # Start an instance of an iperf server using
+            # a unique port. Save the iperf cmdline for
+            # terminating the iperf_server process after the test.
+            self.iperf_cmd = start_iperf(
+                ip_version=6,
+                client_ns=self.client_namespace,
+                server_ns=self.server_namespace,
+                server_only=True,
+                server_args=f"-p {self.iperf_port}",
+                logger=self.logger,
+            )
+            # Send traffic between iperf client & server
             self.assertTrue(
                 start_iperf(
                     ip_version=ip_version,
+                    client_ns=self.client_namespace,
+                    server_ns=self.server_namespace,
                     server_ipv4_address=self.server_ip4_address,
                     server_ipv6_address=self.server_ip6_address,
+                    client_args=f"-p {self.iperf_port}",
                     client_only=True,
                     duration=2,
                     logger=self.logger,
@@ -107,8 +135,17 @@ def create_test(test_name, test, ip_version, mtu):
     return test_func
 
 
-def generate_vpp_interface_tests():
-    """Generate unittests for testing vpp interfaces."""
+def generate_vpp_interface_tests(tests, test_class):
+    """Generate unittests for testing vpp interfaces
+
+    Generates unittests from test spec. and sets them as attributes
+    to the test_class.
+    Args:
+       tests      : list of test specs from vm_test_config['tests']
+       test_class : the name of the test class to which the
+                    generated tests are set as attributes.
+    """
+
     if config.skip_netns_tests:
         print("Skipping netns tests")
     for test in tests:
@@ -132,11 +169,11 @@ def generate_vpp_interface_tests():
                 test_func = create_test(
                     test_name=test_name, test=test, ip_version=ip_version, mtu=mtu
                 )
-                setattr(TestVPPInterfacesQemu, test_name, test_func)
+                setattr(test_class, test_name, test_func)
 
 
 @tag_fixme_debian11
-class TestVPPInterfacesQemu(VppTestCase):
+class TestVPPInterfacesQemu:
     """Test VPP interfaces inside a QEMU VM for IPv4/v6.
 
     Test Setup:
@@ -144,14 +181,6 @@ class TestVPPInterfacesQemu(VppTestCase):
              --vppaf_packet_int2--host-int2--iperfServer--Linux_ns2
     """
 
-    @classmethod
-    def setUpClass(cls):
-        super(TestVPPInterfacesQemu, cls).setUpClass()
-
-    @classmethod
-    def tearDownClass(cls):
-        super(TestVPPInterfacesQemu, cls).tearDownClass()
-
     def setUpTestToplogy(self, test, ip_version):
         """Setup the test topology.
 
@@ -160,7 +189,7 @@ class TestVPPInterfacesQemu(VppTestCase):
         3. Enable desired vif features such as GSO & GRO.
         3. Cross-Connect interfaces in VPP using L2 or L3.
         """
-        super(TestVPPInterfacesQemu, self).setUp()
+
         # Need to support multiple interface types as the memif interface
         # in VPP is connected to the iPerf client & server by x-connecting
         # to a tap interface in their respective namespaces.
@@ -195,7 +224,13 @@ class TestVPPInterfacesQemu(VppTestCase):
             else layer3["vpp_server_ip6_prefix"]
         )
         vpp_server_nexthop = str(ip_interface(vpp_server_prefix).ip)
-        create_namespace([client_namespace, server_namespace])
+        # Create unique namespaces for iperf client & iperf server to
+        # prevent conflicts when TEST_JOBS > 1
+        self.client_namespace = test_config["client_namespace"] + str(test["id"])
+        self.server_namespace = test_config["server_namespace"] + str(test["id"])
+        create_namespace([self.client_namespace, self.server_namespace])
+        # Set a unique iPerf port for parallel server and client runs
+        self.iperf_port = 5000 + test["id"]
         # IPerf client & server ingress/egress interface indexes in VPP
         self.tap_interfaces = []
         self.memif_interfaces = []
@@ -209,13 +244,28 @@ class TestVPPInterfacesQemu(VppTestCase):
         enable_server_if_gro = test.get("server_if_gro", 0)
         enable_client_if_checksum_offload = test.get("client_if_checksum_offload", 0)
         enable_server_if_checksum_offload = test.get("server_if_checksum_offload", 0)
-        ## Handle client interface types
+
+        # Create unique host interfaces in Linux and VPP for connecting to iperf
+        # client & iperf server to prevent conflicts when TEST_JOBS > 1
+        self.iprf_client_host_interface_on_linux = af_packet_config[
+            "iprf_client_interface_on_linux"
+        ] + str(test["id"])
+        self.iprf_client_host_interface_on_vpp = af_packet_config[
+            "iprf_client_interface_on_vpp"
+        ] + str(test["id"])
+        self.iprf_server_host_interface_on_linux = af_packet_config[
+            "iprf_server_interface_on_linux"
+        ] + str(test["id"])
+        self.iprf_server_host_interface_on_vpp = af_packet_config[
+            "iprf_server_interface_on_vpp"
+        ] + str(test["id"])
+        # Handle client interface types
         for client_if_type in client_if_types:
             if client_if_type == "af_packet":
                 create_host_interface(
-                    af_packet_config["iprf_client_interface_on_linux"],
-                    af_packet_config["iprf_client_interface_on_vpp"],
-                    client_namespace,
+                    self.iprf_client_host_interface_on_linux,
+                    self.iprf_client_host_interface_on_vpp,
+                    self.client_namespace,
                     layer2["client_ip4_prefix"]
                     if x_connect_mode == "L2"
                     else layer3["client_ip4_prefix"],
@@ -225,32 +275,30 @@ class TestVPPInterfacesQemu(VppTestCase):
                 )
                 self.ingress_if_idx = self.create_af_packet(
                     version=client_if_version,
-                    host_if_name=af_packet_config["iprf_client_interface_on_vpp"],
+                    host_if_name=self.iprf_client_host_interface_on_vpp,
                     enable_gso=enable_client_if_gso,
                 )
                 self.ingress_if_idxes.append(self.ingress_if_idx)
                 self.vpp_interfaces.append(self.ingress_if_idx)
                 self.linux_interfaces.append(
-                    ["", af_packet_config["iprf_client_interface_on_vpp"]]
+                    ["", self.iprf_client_host_interface_on_vpp]
                 )
                 self.linux_interfaces.append(
                     [
-                        client_namespace,
-                        af_packet_config["iprf_client_interface_on_linux"],
+                        self.client_namespace,
+                        self.iprf_client_host_interface_on_linux,
                     ]
                 )
                 if enable_client_if_gso == 0:
+                    disable_interface_gso("", self.iprf_client_host_interface_on_vpp)
                     disable_interface_gso(
-                        "", af_packet_config["iprf_client_interface_on_vpp"]
-                    )
-                    disable_interface_gso(
-                        client_namespace,
-                        af_packet_config["iprf_client_interface_on_linux"],
+                        self.client_namespace,
+                        self.iprf_client_host_interface_on_linux,
                     )
             elif client_if_type == "tap" or client_if_type == "tun":
                 self.ingress_if_idx = self.create_tap_tun(
                     id=101,
-                    host_namespace=client_namespace,
+                    host_namespace=self.client_namespace,
                     ip_version=ip_version,
                     host_ip4_prefix=layer2["client_ip4_prefix"]
                     if x_connect_mode == "L2"
@@ -273,9 +321,11 @@ class TestVPPInterfacesQemu(VppTestCase):
                 self.tap_interfaces.append(self.ingress_if_idx)
                 self.ingress_if_idxes.append(self.ingress_if_idx)
                 self.vpp_interfaces.append(self.ingress_if_idx)
-                self.linux_interfaces.append([client_namespace, f"{client_if_type}0"])
+                self.linux_interfaces.append(
+                    [self.client_namespace, f"{client_if_type}0"]
+                )
                 # Seeing TCP timeouts if tx=on & rx=on Linux tap & tun interfaces
-                disable_interface_gso(client_namespace, f"{client_if_type}0")
+                disable_interface_gso(self.client_namespace, f"{client_if_type}0")
             elif client_if_type == "memif":
                 self.ingress_if_idx = self.create_memif(
                     memif_id=0, mode=0 if x_connect_mode == "L2" else 1
@@ -292,40 +342,38 @@ class TestVPPInterfacesQemu(VppTestCase):
         for server_if_type in server_if_types:
             if server_if_type == "af_packet":
                 create_host_interface(
-                    af_packet_config["iprf_server_interface_on_linux"],
-                    af_packet_config["iprf_server_interface_on_vpp"],
-                    server_namespace,
+                    self.iprf_server_host_interface_on_linux,
+                    self.iprf_server_host_interface_on_vpp,
+                    self.server_namespace,
                     server_ip4_prefix,
                     server_ip6_prefix,
                 )
                 self.egress_if_idx = self.create_af_packet(
                     version=server_if_version,
-                    host_if_name=af_packet_config["iprf_server_interface_on_vpp"],
+                    host_if_name=self.iprf_server_host_interface_on_vpp,
                     enable_gso=enable_server_if_gso,
                 )
                 self.egress_if_idxes.append(self.egress_if_idx)
                 self.vpp_interfaces.append(self.egress_if_idx)
                 self.linux_interfaces.append(
-                    ["", af_packet_config["iprf_server_interface_on_vpp"]]
+                    ["", self.iprf_server_host_interface_on_vpp]
                 )
                 self.linux_interfaces.append(
                     [
-                        server_namespace,
-                        af_packet_config["iprf_server_interface_on_linux"],
+                        self.server_namespace,
+                        self.iprf_server_host_interface_on_linux,
                     ]
                 )
                 if enable_server_if_gso == 0:
+                    disable_interface_gso("", self.iprf_server_host_interface_on_vpp)
                     disable_interface_gso(
-                        "", af_packet_config["iprf_server_interface_on_vpp"]
-                    )
-                    disable_interface_gso(
-                        server_namespace,
-                        af_packet_config["iprf_server_interface_on_linux"],
+                        self.server_namespace,
+                        self.iprf_server_host_interface_on_linux,
                     )
             elif server_if_type == "tap" or server_if_type == "tun":
                 self.egress_if_idx = self.create_tap_tun(
                     id=102,
-                    host_namespace=server_namespace,
+                    host_namespace=self.server_namespace,
                     ip_version=ip_version,
                     host_ip4_prefix=layer2["server_ip4_prefix"]
                     if x_connect_mode == "L2"
@@ -342,9 +390,11 @@ class TestVPPInterfacesQemu(VppTestCase):
                 self.tap_interfaces.append(self.egress_if_idx)
                 self.egress_if_idxes.append(self.egress_if_idx)
                 self.vpp_interfaces.append(self.egress_if_idx)
-                self.linux_interfaces.append([server_namespace, f"{server_if_type}0"])
+                self.linux_interfaces.append(
+                    [self.server_namespace, f"{server_if_type}0"]
+                )
                 # Seeing TCP timeouts if tx=on & rx=on Linux tap & tun interfaces
-                disable_interface_gso(server_namespace, f"{server_if_type}0")
+                disable_interface_gso(self.server_namespace, f"{server_if_type}0")
             elif server_if_type == "memif":
                 self.egress_if_idx = self.create_memif(
                     memif_id=1, mode=0 if x_connect_mode == "L2" else 1
@@ -375,14 +425,18 @@ class TestVPPInterfacesQemu(VppTestCase):
                 # Setup namespace routing
                 if ip_version == 4:
                     add_namespace_route(
-                        client_namespace, "0.0.0.0/0", vpp_client_nexthop
+                        self.client_namespace, "0.0.0.0/0", vpp_client_nexthop
                     )
                     add_namespace_route(
-                        server_namespace, "0.0.0.0/0", vpp_server_nexthop
+                        self.server_namespace, "0.0.0.0/0", vpp_server_nexthop
                     )
                 else:
-                    add_namespace_route(client_namespace, "::/0", vpp_client_nexthop)
-                    add_namespace_route(server_namespace, "::/0", vpp_server_nexthop)
+                    add_namespace_route(
+                        self.client_namespace, "::/0", vpp_client_nexthop
+                    )
+                    add_namespace_route(
+                        self.server_namespace, "::/0", vpp_server_nexthop
+                    )
         else:
             # connect: ingress tap & memif & egress tap and memif interfaces
             if x_connect_mode == "L2":
@@ -405,28 +459,18 @@ class TestVPPInterfacesQemu(VppTestCase):
             pass
         try:
             for interface in self.vapi.af_packet_dump():
-                if (
-                    interface.host_if_name
-                    == af_packet_config["iprf_client_interface_on_vpp"]
-                ):
-                    self.vapi.af_packet_delete(
-                        af_packet_config["iprf_client_interface_on_vpp"]
-                    )
-                elif (
-                    interface.host_if_name
-                    == af_packet_config["iprf_server_interface_on_vpp"]
-                ):
-                    self.vapi.af_packet_delete(
-                        af_packet_config["iprf_server_interface_on_vpp"]
-                    )
+                if interface.host_if_name == self.iprf_client_host_interface_on_vpp:
+                    self.vapi.af_packet_delete(self.iprf_client_host_interface_on_vpp)
+                elif interface.host_if_name == self.iprf_server_host_interface_on_vpp:
+                    self.vapi.af_packet_delete(self.iprf_server_host_interface_on_vpp)
         except Exception:
             pass
         try:
             delete_host_interfaces(
-                af_packet_config["iprf_client_interface_on_linux"],
-                af_packet_config["iprf_server_interface_on_linux"],
-                af_packet_config["iprf_client_interface_on_vpp"],
-                af_packet_config["iprf_server_interface_on_vpp"],
+                self.iprf_client_host_interface_on_linux,
+                self.iprf_server_host_interface_on_linux,
+                self.iprf_client_host_interface_on_vpp,
+                self.iprf_server_host_interface_on_vpp,
             )
         except Exception:
             pass
@@ -446,14 +490,15 @@ class TestVPPInterfacesQemu(VppTestCase):
         try:
             delete_namespace(
                 [
-                    client_namespace,
-                    server_namespace,
+                    self.client_namespace,
+                    self.server_namespace,
                 ]
             )
         except Exception:
             pass
         try:
-            stop_iperf()
+            if hasattr(self, "iperf_cmd"):
+                stop_iperf(" ".join(self.iperf_cmd))
         except Exception:
             pass
         try:
@@ -675,7 +720,5 @@ class TestVPPInterfacesQemu(VppTestCase):
             return False
 
 
-generate_vpp_interface_tests()
-
 if __name__ == "__main__":
     unittest.main(testRunner=VppTestRunner)