Revert "fix(IPsecUtil): Delete keywords no longer used"
[csit.git] / resources / libraries / python / HoststackUtil.py
index e797c3c..399395d 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2021 Cisco and/or its affiliates.
+# Copyright (c) 2023 Cisco and/or its affiliates.
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at:
@@ -17,9 +17,12 @@ from time import sleep
 from robot.api import logger
 
 from resources.libraries.python.Constants import Constants
-from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
-from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.DUTSetup import DUTSetup
+from resources.libraries.python.model.ExportResult import (
+    export_hoststack_results
+)
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
+from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
 
 class HoststackUtil():
     """Utilities for Host Stack tests."""
@@ -35,14 +38,13 @@ class HoststackUtil():
             'args' - command arguments.
         :rtype: dict
         """
-        # TODO: Use a python class instead of dictionary for the return type
         proto = vpp_echo_attributes[u"uri_protocol"]
         addr = vpp_echo_attributes[u"uri_ip4_addr"]
         port = vpp_echo_attributes[u"uri_port"]
         vpp_echo_cmd = {}
         vpp_echo_cmd[u"name"] = u"vpp_echo"
         vpp_echo_cmd[u"args"] = f"{vpp_echo_attributes[u'role']} " \
-            f"socket-name {vpp_echo_attributes[u'vpp_api_socket']} " \
+            f"socket-name {vpp_echo_attributes[u'app_api_socket']} " \
             f"{vpp_echo_attributes[u'json_output']} " \
             f"uri {proto}://{addr}/{port} " \
             f"nthreads {vpp_echo_attributes[u'nthreads']} " \
@@ -57,6 +59,8 @@ class HoststackUtil():
             vpp_echo_cmd[u"args"] += u" rx-results-diff"
         if vpp_echo_attributes[u"tx_results_diff"]:
             vpp_echo_cmd[u"args"] += u" tx-results-diff"
+        if vpp_echo_attributes[u"use_app_socket_api"]:
+            vpp_echo_cmd[u"args"] += u" use-app-socket-api"
         return vpp_echo_cmd
 
     @staticmethod
@@ -71,7 +75,6 @@ class HoststackUtil():
             'args' - command arguments.
         :rtype: dict
         """
-        # TODO: Use a python class instead of dictionary for the return type
         iperf3_cmd = {}
         iperf3_cmd[u"env_vars"] = f"VCL_CONFIG={Constants.REMOTE_FW_DIR}/" \
             f"{Constants.RESOURCES_TPL_VCL}/" \
@@ -100,6 +103,13 @@ class HoststackUtil():
             if u"time" in iperf3_attributes:
                 iperf3_cmd[u"args"] += \
                     f" --time {iperf3_attributes[u'time']}"
+            if iperf3_attributes[u"udp"]:
+                iperf3_cmd[u"args"] += u" --udp"
+                iperf3_cmd[u"args"] += \
+                    f" --bandwidth {iperf3_attributes[u'bandwidth']}"
+            if iperf3_attributes[u"length"] > 0:
+                iperf3_cmd[u"args"] += \
+                    f" --length {iperf3_attributes[u'length']}"
         return iperf3_cmd
 
     @staticmethod
@@ -146,15 +156,14 @@ class HoststackUtil():
                     raise
 
     @staticmethod
-    def get_hoststack_test_program_logs(node, program):
+    def _get_hoststack_test_program_logs(node, program_name):
         """Get HostStack test program stdout log.
 
         :param node: DUT node.
-        :param program: test program.
+        :param program_name: test program.
         :type node: dict
-        :type program: dict
+        :type program_name: str
         """
-        program_name = program[u"name"]
         cmd = f"sh -c \'cat /tmp/{program_name}_stdout.log\'"
         stdout_log, _ = exec_cmd_no_error(node, cmd, sudo=True, \
             message=f"Get {program_name} stdout log failed!")
@@ -162,8 +171,29 @@ class HoststackUtil():
         cmd = f"sh -c \'cat /tmp/{program_name}_stderr.log\'"
         stderr_log, _ = exec_cmd_no_error(node, cmd, sudo=True, \
             message=f"Get {program_name} stderr log failed!")
+
         return stdout_log, stderr_log
 
+    @staticmethod
+    def get_hoststack_test_program_logs(node, program):
+        """Get HostStack test program stdout log.
+
+        :param node: DUT node.
+        :param program: test program.
+        :type node: dict
+        :type program: dict
+        """
+        program_name = program[u"name"]
+        program_stdout_log, program_stderr_log = \
+            HoststackUtil._get_hoststack_test_program_logs(node,
+                                                           program_name)
+        if len(program_stdout_log) == 0 and len(program_stderr_log) == 0:
+            logger.trace(f"Retrying {program_name} log retrieval")
+            program_stdout_log, program_stderr_log = \
+               HoststackUtil._get_hoststack_test_program_logs(node,
+                                                              program_name)
+        return program_stdout_log, program_stderr_log
+
     @staticmethod
     def get_nginx_command(nginx_attributes, nginx_version, nginx_ins_dir):
         """Construct the NGINX command using the specified attributes.
@@ -268,22 +298,69 @@ class HoststackUtil():
         exec_cmd_no_error(node, cmd, message=errmsg, sudo=True)
 
     @staticmethod
-    def hoststack_test_program_finished(node, program_pid):
+    def hoststack_test_program_finished(node, program_pid, program,
+                                        other_node, other_program):
         """Wait for the specified HostStack test program process to complete.
 
         :param node: DUT node.
         :param program_pid: test program pid.
+        :param program: test program
+        :param other_node: DUT node of other hoststack program
+        :param other_program: other test program
         :type node: dict
         :type program_pid: str
+        :type program: dict
+        :type other_node: dict
+        :type other_program: dict
         :raises RuntimeError: If node subtype is not a DUT.
         """
         if node[u"type"] != u"DUT":
             raise RuntimeError(u"Node type is not a DUT!")
+        if other_node[u"type"] != u"DUT":
+            raise RuntimeError(u"Other node type is not a DUT!")
 
         cmd = f"sh -c 'strace -qqe trace=none -p {program_pid}'"
-        exec_cmd(node, cmd, sudo=True)
+        try:
+            exec_cmd(node, cmd, sudo=True)
+        except:
+            sleep(180)
+            if u"client" in program[u"args"]:
+                role = u"client"
+            else:
+                role = u"server"
+            program_stdout, program_stderr = \
+                HoststackUtil.get_hoststack_test_program_logs(node, program)
+            if len(program_stdout) > 0:
+                logger.debug(f"{program[u'name']} {role} stdout log:\n"
+                             f"{program_stdout}")
+            else:
+                logger.debug(f"Empty {program[u'name']} {role} stdout log :(")
+            if len(program_stderr) > 0:
+                logger.debug(f"{program[u'name']} stderr log:\n"
+                             f"{program_stderr}")
+            else:
+                logger.debug(f"Empty {program[u'name']} stderr log :(")
+            if u"client" in other_program[u"args"]:
+                role = u"client"
+            else:
+                role = u"server"
+            program_stdout, program_stderr = \
+                HoststackUtil.get_hoststack_test_program_logs(other_node,
+                                                              other_program)
+            if len(program_stdout) > 0:
+                logger.debug(f"{other_program[u'name']} {role} stdout log:\n"
+                             f"{program_stdout}")
+            else:
+                logger.debug(f"Empty {other_program[u'name']} "
+                             f"{role} stdout log :(")
+            if len(program_stderr) > 0:
+                logger.debug(f"{other_program[u'name']} {role} stderr log:\n"
+                             f"{program_stderr}")
+            else:
+                logger.debug(f"Empty {other_program[u'name']} "
+                             f"{role} stderr log :(")
+            raise
         # Wait a bit for stdout/stderr to be flushed to log files
-        # TODO: see if sub-second sleep works e.g. sleep(0.1)
         sleep(1)
 
     @staticmethod
@@ -317,10 +394,6 @@ class HoststackUtil():
         program_name = program[u"name"]
         program_stdout, program_stderr = \
             HoststackUtil.get_hoststack_test_program_logs(node, program)
-        if len(program_stdout) == 0 and len(program_stderr) == 0:
-            logger.trace(f"Retrying {program_name} log retrieval")
-            program_stdout, program_stderr = \
-               HoststackUtil.get_hoststack_test_program_logs(node, program)
 
         env_vars = f"{program[u'env_vars']} " if u"env_vars" in program else u""
         program_cmd = f"{env_vars}{program_name} {program[u'args']}"
@@ -340,7 +413,6 @@ class HoststackUtil():
                 f"bits/sec, pkt-drop-rate {nsim_attr[u'packets_per_drop']} " \
                 f"pkts/drop\n"
 
-        # TODO: Incorporate show error stats into results analysis
         test_results += \
             f"\n{role} VPP 'show errors' on host {node[u'host']}:\n" \
             f"{PapiSocketExecutor.run_cli_cmd(node, u'show error')}\n"
@@ -358,18 +430,28 @@ class HoststackUtil():
             if u"JSON stats" in program_stdout and \
                     u'"has_failed": "0"' in program_stdout:
                 json_start = program_stdout.find(u"{")
-                #TODO: Fix parsing once vpp_echo produces valid
-                # JSON output. Truncate for now.
                 json_end = program_stdout.find(u',\n  "closing"')
                 json_results = f"{program_stdout[json_start:json_end]}\n}}"
                 program_json = json.loads(json_results)
+                export_hoststack_results(
+                    bandwidth=program_json["rx_bits_per_second"],
+                    duration=float(program_json["time"])
+                )
             else:
                 test_results += u"Invalid test data output!\n" + program_stdout
                 return (True, test_results)
         elif program[u"name"] == u"iperf3":
             test_results += program_stdout
-            iperf3_json = json.loads(program_stdout)
-            program_json = iperf3_json[u"intervals"][0][u"sum"]
+            program_json = json.loads(program_stdout)[u"intervals"][0][u"sum"]
+            try:
+                retransmits = program_json["retransmits"]
+            except KeyError:
+                retransmits = None
+            export_hoststack_results(
+                bandwidth=program_json["bits_per_second"],
+                duration=program_json["seconds"],
+                retransmits=retransmits
+            )
         else:
             test_results += u"Unknown HostStack Test Program!\n" + \
                             program_stdout