tests: Add support for getting corefile patterns on FreeBSD
[vpp.git] / test / vpp_qemu_utils.py
index 50fc1c8..03b8632 100644 (file)
 # Utility functions for QEMU tests ##
 
 import subprocess
+import sys
+import os
+import multiprocessing as mp
+
+
+def can_create_namespaces(namespace="vpp_chk_4212"):
+    """Check if the environment allows creating the namespaces"""
+
+    try:
+        result = subprocess.run(["ip", "netns", "add", namespace], capture_output=True)
+        if result.returncode != 0:
+            return False
+        result = subprocess.run(["ip", "netns", "del", namespace], capture_output=True)
+        if result.returncode != 0:
+            return False
+        return True
+    except Exception:
+        return False
 
 
 def create_namespace(ns):
+    """create one or more namespaces.
+
+    arguments:
+    ns -- a string value or an iterable of namespace names
+    """
+    if isinstance(ns, str):
+        namespaces = [ns]
+    else:
+        namespaces = ns
     try:
-        subprocess.run(["ip", "netns", "add", ns])
+        for namespace in namespaces:
+            result = subprocess.run(["ip", "netns", "add", namespace])
+            if result.returncode != 0:
+                raise Exception(f"Error while creating namespace {namespace}")
     except subprocess.CalledProcessError as e:
         raise Exception("Error creating namespace:", e.output)
 
 
+def add_namespace_route(ns, prefix, gw_ip):
+    """Add a route to a namespace.
+
+    arguments:
+    ns -- namespace string value
+    prefix -- NETWORK/MASK or "default"
+    gw_ip -- Gateway IP
+    """
+    try:
+        subprocess.run(
+            ["ip", "netns", "exec", ns, "ip", "route", "add", prefix, "via", gw_ip],
+            capture_output=True,
+        )
+    except subprocess.CalledProcessError as e:
+        raise Exception("Error adding route to namespace:", e.output)
+
+
+def delete_host_interfaces(*host_interface_names):
+    """Delete host interfaces.
+
+    arguments:
+    host_interface_names - sequence of host interface names to be deleted
+    """
+    for host_interface_name in host_interface_names:
+        try:
+            subprocess.run(
+                ["ip", "link", "del", host_interface_name], capture_output=True
+            )
+        except subprocess.CalledProcessError as e:
+            raise Exception("Error deleting host interface:", e.output)
+
+
+def create_host_interface(
+    host_interface_name, vpp_interface_name, host_namespace, *host_ip_prefixes
+):
+    """Create a host interface of type veth.
+
+    arguments:
+    host_interface_name -- name of the veth interface on the host side
+    vpp_interface_name -- name of the veth interface on the VPP side
+    host_namespace -- host namespace into which the host_interface needs to be set
+    host_ip_prefixes -- a sequence of ip/prefix-lengths to be set
+                        on the host_interface
+    """
+    try:
+        process = subprocess.run(
+            [
+                "ip",
+                "link",
+                "add",
+                "name",
+                vpp_interface_name,
+                "type",
+                "veth",
+                "peer",
+                "name",
+                host_interface_name,
+            ],
+            capture_output=True,
+        )
+        if process.returncode != 0:
+            print(f"Error creating host interface: {process.stderr}")
+            sys.exit(1)
+
+        process = subprocess.run(
+            ["ip", "link", "set", host_interface_name, "netns", host_namespace],
+            capture_output=True,
+        )
+        if process.returncode != 0:
+            print(f"Error setting host interface namespace: {process.stderr}")
+            sys.exit(1)
+
+        process = subprocess.run(
+            ["ip", "link", "set", "dev", vpp_interface_name, "up"], capture_output=True
+        )
+        if process.returncode != 0:
+            print(f"Error bringing up the host interface: {process.stderr}")
+            sys.exit(1)
+
+        process = subprocess.run(
+            [
+                "ip",
+                "netns",
+                "exec",
+                host_namespace,
+                "ip",
+                "link",
+                "set",
+                "dev",
+                host_interface_name,
+                "up",
+            ],
+            capture_output=True,
+        )
+        if process.returncode != 0:
+            print(
+                f"Error bringing up the host interface in namespace: "
+                f"{process.stderr}"
+            )
+            sys.exit(1)
+
+        for host_ip_prefix in host_ip_prefixes:
+            process = subprocess.run(
+                [
+                    "ip",
+                    "netns",
+                    "exec",
+                    host_namespace,
+                    "ip",
+                    "addr",
+                    "add",
+                    host_ip_prefix,
+                    "dev",
+                    host_interface_name,
+                ],
+                capture_output=True,
+            )
+            if process.returncode != 0:
+                print(
+                    f"Error setting ip prefix on the host interface: "
+                    f"{process.stderr}"
+                )
+                sys.exit(1)
+    except subprocess.CalledProcessError as e:
+        raise Exception("Error adding route to namespace:", e.output)
+
+
+def set_interface_mtu(namespace, interface, mtu, logger):
+    """set an mtu number on a linux device interface."""
+    args = ["ip", "link", "set", "mtu", str(mtu), "dev", interface]
+    if namespace:
+        args = ["ip", "netns", "exec", namespace] + args
+    try:
+        logger.debug(
+            f"Setting mtu:{mtu} on linux interface:{interface} "
+            f"in namespace:{namespace}"
+        )
+        subprocess.run(args)
+    except subprocess.CalledProcessError as e:
+        raise Exception("Error updating mtu:", e.output)
+
+
+def enable_interface_gso(namespace, interface):
+    """enable gso offload on a linux device interface."""
+    args = ["ethtool", "-K", interface, "rx", "on", "tx", "on"]
+    if namespace:
+        args = ["ip", "netns", "exec", namespace] + args
+    try:
+        process = subprocess.run(args, capture_output=True)
+        if process.returncode != 0:
+            print(
+                f"Error enabling GSO offload on linux device interface: "
+                f"{process.stderr}"
+            )
+            sys.exit(1)
+    except subprocess.CalledProcessError as e:
+        raise Exception("Error enabling gso:", e.output)
+
+
+def disable_interface_gso(namespace, interface):
+    """disable gso offload on a linux device interface."""
+    args = ["ethtool", "-K", interface, "rx", "off", "tx", "off"]
+    if namespace:
+        args = ["ip", "netns", "exec", namespace] + args
+    try:
+        process = subprocess.run(args, capture_output=True)
+        if process.returncode != 0:
+            print(
+                f"Error disabling GSO offload on linux device interface: "
+                f"{process.stderr}"
+            )
+            sys.exit(1)
+    except subprocess.CalledProcessError as e:
+        raise Exception("Error disabling gso:", e.output)
+
+
+def delete_namespace(ns):
+    """delete one or more namespaces.
+
+    arguments:
+    namespaces -- a list of namespace names
+    """
+    if isinstance(ns, str):
+        namespaces = [ns]
+    else:
+        namespaces = ns
+    try:
+        for namespace in namespaces:
+            result = subprocess.run(
+                ["ip", "netns", "del", namespace], capture_output=True
+            )
+            if result.returncode != 0:
+                raise Exception(f"Error while deleting namespace {namespace}")
+    except subprocess.CalledProcessError as e:
+        raise Exception("Error deleting namespace:", e.output)
+
+
 def list_namespace(ns):
     """List the IP address of a namespace"""
     try:
         subprocess.run(["ip", "netns", "exec", ns, "ip", "addr"])
     except subprocess.CalledProcessError as e:
         raise Exception("Error listing namespace IP:", e.output)
+
+
+def libmemif_test_app(memif_sock_path, logger):
+    """Build & run the libmemif test_app for memif interface testing."""
+    test_dir = os.path.dirname(os.path.realpath(__file__))
+    ws_root = os.path.dirname(test_dir)
+    libmemif_app = os.path.join(
+        ws_root, "extras", "libmemif", "build", "examples", "test_app"
+    )
+
+    def build_libmemif_app():
+        if not os.path.exists(libmemif_app):
+            print(f"Building app:{libmemif_app} for memif interface testing")
+            libmemif_app_dir = os.path.join(ws_root, "extras", "libmemif", "build")
+            if not os.path.exists(libmemif_app_dir):
+                os.makedirs(libmemif_app_dir)
+            os.chdir(libmemif_app_dir)
+            try:
+                p = subprocess.run(["cmake", ".."], capture_output=True)
+                logger.debug(p.stdout)
+                if p.returncode != 0:
+                    print(f"libmemif app:{libmemif_app} cmake error:{p.stderr}")
+                    sys.exit(1)
+                p = subprocess.run(["make"], capture_output=True)
+                logger.debug(p.stdout)
+                if p.returncode != 0:
+                    print(f"Error building libmemif app:{p.stderr}")
+                    sys.exit(1)
+            except subprocess.CalledProcessError as e:
+                raise Exception("Error building libmemif_test_app:", e.output)
+
+    def start_libmemif_app():
+        """Restart once if the initial run fails."""
+        max_tries = 2
+        run = 0
+        if not os.path.exists(libmemif_app):
+            raise Exception(
+                f"Error could not locate the libmemif test app:{libmemif_app}"
+            )
+        args = [libmemif_app, "-b", "9216", "-s", memif_sock_path]
+        while run < max_tries:
+            try:
+                process = subprocess.run(args, capture_output=True)
+                logger.debug(process.stdout)
+                if process.returncode != 0:
+                    msg = f"Error starting libmemif app:{libmemif_app}"
+                    logger.error(msg)
+                    raise Exception(msg)
+            except Exception:
+                msg = f"re-starting libmemif app:{libmemif_app}"
+                logger.error(msg)
+                continue
+            else:
+                break
+            finally:
+                run += 1
+
+    build_libmemif_app()
+    process = mp.Process(target=start_libmemif_app)
+    process.start()
+    return process