#!/usr/bin/env python # 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: 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