tests: Use errno value rather than a specific int
[vpp.git] / test / vpp_iperf.py
index 78ce9d0..b325399 100644 (file)
@@ -4,6 +4,7 @@
 
 import subprocess
 import os
+import sys
 
 
 class VppIperf:
@@ -26,15 +27,16 @@ class VppIperf:
     iperf.server_args='-p 5202'
     """
 
-    def __init__(self, server_ns=None, client_ns=None, server_ip=None):
+    def __init__(self, server_ns=None, client_ns=None, server_ip=None, logger=None):
         self.server_ns = server_ns
         self.client_ns = client_ns
         self.server_ip = server_ip
         self.duration = 10
         self.client_args = ""
         self.server_args = ""
+        self.logger = logger
         # Set the iperf executable
-        self.iperf = os.path.join(os.getenv("TEST_DATA_DIR") or "/", "usr/bin/iperf")
+        self.iperf = self.get_iperf()
 
     def ensure_init(self):
         if self.server_ns and self.client_ns and self.server_ip:
@@ -44,8 +46,26 @@ class VppIperf:
                 "Error: Cannot Start." "iPerf object has not been initialized"
             )
 
+    def get_iperf(self):
+        """Return the iperf executable for running tests.
+
+        Look for the iperf executable in the following order
+        1. ${TEST_DATA_DIR}/usr/bin/iperf  # running tests inside the VM
+        2. /usr/bin/iperf3                 # running tests on the host
+        """
+        vm_test_dir = os.getenv("TEST_DATA_DIR", "/tmp/vpp-vm-tests")
+        if os.path.isdir(vm_test_dir):
+            iperf = os.path.join(vm_test_dir, "/usr/bin/iperf")
+        else:
+            iperf = "/usr/bin/iperf3"
+        if os.path.exists(iperf):
+            return iperf
+        else:
+            self.logger.error(f"Could not find an iperf executable for running tests")
+            sys.exit(1)
+
     def start_iperf_server(self):
-        print("Starting iPerf Server Daemon in Namespace ", self.server_ns)
+        """Starts the  iperf server and returns the process cmdline args."""
         args = [
             "ip",
             "netns",
@@ -54,22 +74,24 @@ class VppIperf:
             self.iperf,
             "-s",
             "-D",
-            "-B",
-            self.server_ip,
         ]
         args.extend(self.server_args.split())
+        cmd = " ".join(args)
+        self.logger.debug(f"Starting iperf server: {cmd}")
         try:
             subprocess.run(
-                args,
-                stderr=subprocess.STDOUT,
+                cmd,
                 timeout=self.duration + 5,
                 encoding="utf-8",
+                shell=True,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
             )
         except subprocess.TimeoutExpired as e:
             raise Exception("Error: Timeout expired for iPerf", e.output)
+        return args[4:]
 
     def start_iperf_client(self):
-        print("Starting iPerf Client in Namespace ", self.client_ns)
         args = [
             "ip",
             "netns",
@@ -82,30 +104,112 @@ class VppIperf:
             str(self.duration),
         ]
         args.extend(self.client_args.split())
+        args = " ".join(args)
         try:
-            subprocess.run(
+            return subprocess.run(
                 args,
-                stderr=subprocess.STDOUT,
                 timeout=self.duration + 5,
                 encoding="utf-8",
+                capture_output=True,
+                shell=True,
             )
         except subprocess.TimeoutExpired as e:
             raise Exception("Error: Timeout expired for iPerf", e.output)
 
-    def start(self):
-        """Run iPerf and return True if successful"""
+    def start(self, server_only=False, client_only=False):
+        """Runs iPerf.
+
+        Starts the iperf server daemon & runs the iperf client.
+        arguments:-
+        server_only -- start the iperf server daemon only
+        client_only -- run the iperf client only
+        Return True if we have no errors in iPerf client, else False.
+        """
         self.ensure_init()
-        try:
-            self.start_iperf_server()
-        except Exception as e:
-            subprocess.run(["pkill", "iperf"])
-            raise Exception("Error starting iPerf Server", e)
+        if not client_only:
+            return self.start_iperf_server()
+        if not server_only:
+            result = self.start_iperf_client()
+            self.logger.debug(f"Iperf client args: {result.args}")
+            self.logger.debug(result.stdout)
+            if result.stderr:
+                self.logger.error(
+                    f"Error starting Iperf Client in Namespace: {self.client_ns}"
+                )
+                self.logger.error(f"Iperf client args: {result.args}")
+                self.logger.error(f"Iperf client has errors: {result.stderr}")
+                return False
+            else:
+                return True
+
+
+## Functions to start and stop iPerf using the iPerf object
+def start_iperf(
+    ip_version,
+    client_ns="iprf_client_ns",
+    server_ns="iprf_server_ns",
+    server_ipv4_address="10.0.0.102",
+    server_ipv6_address="2001:1::2",
+    client_args="",
+    server_args="",
+    duration=10,
+    server_only=False,
+    client_only=False,
+    logger=None,
+):
+    """Start an iperf connection stream using the iPerf object.
+
+    Starts iPerf an connection stream between an iPerf client in the
+    client namespace (client_ns) and a server in another
+    namespace (server_ns).
+    Parameters:
+    ip_version - 4 or 6
+    client_ns - iPerf client namespace
+    server_ns - iPerf server namespace
+    server_ipv4_address - ipv4 address of the server, if ip_version=4
+    server_ipv6_address - ipv6 address of the server, if ip_version=6
+    client_args - Additonal iperf control arguments to be passed
+                    to the iperf client from the test (str)
+    server_args - Additonal iperf control arguments to be passed
+                    to the iperf server from the test (str)
+    duration    - Iperf duration in seconds
+    server_only - start iperf server only
+    client_only - start the iperf client only
+    logger - test logger
+    """
+    if ip_version == 4:
+        iperf_server_ip = server_ipv4_address
+    elif ip_version == 6:
+        iperf_server_ip = server_ipv6_address
+        client_args = "-V" + " " + client_args
+        server_args = "-V" + " " + server_args
+    iperf = VppIperf()
+    iperf.client_ns = client_ns
+    iperf.server_ns = server_ns
+    iperf.server_ip = iperf_server_ip
+    iperf.client_args = client_args
+    iperf.server_args = server_args
+    iperf.duration = duration
+    iperf.logger = logger
+    return iperf.start(server_only=server_only, client_only=client_only)
+
 
+def stop_iperf(iperf_cmd):
+    """Stop the iperf process matching the iperf_cmd string."""
+    args = ["pgrep", "-x", "-f", iperf_cmd]
+    p = subprocess.Popen(
+        args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8"
+    )
+    stdout, _ = p.communicate()
+    for pid in stdout.split():
         try:
-            self.start_iperf_client()
-        except Exception as e:
-            raise Exception("Error starting iPerf Client", e)
-        subprocess.run(["pkill", "iperf"])
+            subprocess.run(
+                f"kill -9 {pid}",
+                encoding="utf-8",
+                shell=True,
+            )
+        except Exception:
+            pass
 
 
 if __name__ == "__main__":