# limitations under the License.
"""Host Stack util library."""
+import json
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
f"socket-name {vpp_echo_attributes[u'vpp_api_socket']} " \
f"{vpp_echo_attributes[u'json_output']} " \
f"uri {proto}://{addr}/{port} " \
+ f"nthreads {vpp_echo_attributes[u'nthreads']} " \
+ f"mq-size {vpp_echo_attributes[u'mq_size']} " \
f"nclients {vpp_echo_attributes[u'nclients']} " \
f"quic-streams {vpp_echo_attributes[u'quic_streams']} " \
f"time {vpp_echo_attributes[u'time']} " \
vpp_echo_cmd[u"args"] += u" tx-results-diff"
return vpp_echo_cmd
+ @staticmethod
+ def get_iperf3_command(iperf3_attributes):
+ """Construct the iperf3 command using the specified attributes.
+
+ :param iperf3_attributes: iperf3 test program attributes.
+ :type iperf3_attributes: dict
+ :returns: Command line components of the iperf3 command
+ 'env_vars' - environment variables
+ 'name' - program name
+ '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}/" \
+ f"{iperf3_attributes[u'vcl_config']}"
+ if iperf3_attributes[u"ld_preload"]:
+ iperf3_cmd[u"env_vars"] += \
+ f" LD_PRELOAD={Constants.VCL_LDPRELOAD_LIBRARY}"
+ if iperf3_attributes[u'transparent_tls']:
+ iperf3_cmd[u"env_vars"] += u" LDP_ENV_TLS_TRANS=1"
+
+ json_results = u" --json" if iperf3_attributes[u'json'] else u""
+ ip_address = f" {iperf3_attributes[u'ip_address']}" if u"ip_address" \
+ in iperf3_attributes else u""
+ iperf3_cmd[u"name"] = u"iperf3"
+ iperf3_cmd[u"args"] = f"--{iperf3_attributes[u'role']}{ip_address} " \
+ f"--interval 0{json_results} " \
+ f"--version{iperf3_attributes[u'ip_version']}"
+
+ if iperf3_attributes[u"role"] == u"server":
+ iperf3_cmd[u"args"] += u" --one-off"
+ else:
+ iperf3_cmd[u"args"] += u" --get-server-output"
+ if u"parallel" in iperf3_attributes:
+ iperf3_cmd[u"args"] += \
+ f" --parallel {iperf3_attributes[u'parallel']}"
+ if u"time" in iperf3_attributes:
+ iperf3_cmd[u"args"] += \
+ f" --time {iperf3_attributes[u'time']}"
+ return iperf3_cmd
+
@staticmethod
def set_hoststack_quic_fifo_size(node, fifo_size):
"""Set the QUIC protocol fifo size.
:type node: dict
:type quic_crypto_engine: str
"""
- vpp_crypto_engines = {u"openssl", u"ia32", u"ipsecmb"}
+ vpp_crypto_engines = {u"openssl", u"native", u"ipsecmb"}
if quic_crypto_engine == u"nocrypto":
logger.trace(u"No QUIC crypto engine.")
return
return stdout_log, stderr_log
@staticmethod
- def start_hoststack_test_program(node, namespace, program):
+ def start_hoststack_test_program(node, namespace, core_list, program):
"""Start the specified HostStack test program.
:param node: DUT node.
:param namespace: Net Namespace to run program in.
+ :param core_list: List of cpu's to pass to taskset to pin the test
+ program to a different set of cores on the same numa node as VPP.
:param program: Test program.
:type node: dict
:type namespace: str
+ :type core_list: str
:type program: dict
:returns: Process ID
:rtype: int
:raises RuntimeError: If node subtype is not a DUT or startup failed.
"""
- # TODO: Pin test program to core(s) on same numa node as VPP.
if node[u"type"] != u"DUT":
raise RuntimeError(u"Node type is not a DUT!")
env_vars = f"{program[u'env_vars']} " if u"env_vars" in program else u""
args = program[u"args"]
- cmd = f"nohup {shell_cmd} \'{env_vars}{program_name} {args} " \
- f">/tmp/{program_name}_stdout.log " \
+ cmd = f"nohup {shell_cmd} \'{env_vars}taskset --cpu-list {core_list} " \
+ f"{program_name} {args} >/tmp/{program_name}_stdout.log " \
f"2>/tmp/{program_name}_stderr.log &\'"
try:
exec_cmd_no_error(node, cmd, sudo=True)
sleep(1)
@staticmethod
- def analyze_hoststack_test_program_output(node, role, nsim_attr,
- program):
+ def analyze_hoststack_test_program_output(
+ node, role, nsim_attr, program):
"""Gather HostStack test program output and check for errors.
+ The [defer_fail] return bool is used instead of failing immediately
+ to allow the analysis of both the client and server instances of
+ the test program for debugging a test failure. When [defer_fail]
+ is true, then the string returned is debug output instead of
+ JSON formatted test program results.
+
:param node: DUT node.
:param role: Role (client|server) of test program.
:param nsim_attr: Network Simulation Attributes.
:type role: str
:type nsim_attr: dict
:type program: dict
- :returns: tuple of no results bool and test program results.
+ :returns: tuple of [defer_fail] bool and either JSON formatted hoststack
+ test program output or failure debug output.
:rtype: bool, str
:raises RuntimeError: If node subtype is not a DUT.
"""
program_stdout, program_stderr = \
HoststackUtil.get_hoststack_test_program_logs(node, program)
- no_results = False
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']}"
test_results = f"Test Results of '{program_cmd}':\n"
- if nsim_attr[u"output_feature_enable"] or \
- nsim_attr[u"cross_connect_feature_enable"]:
- if nsim_attr[u"output_feature_enable"]:
+ if nsim_attr[u"output_nsim_enable"] or \
+ nsim_attr[u"xc_nsim_enable"]:
+ if nsim_attr[u"output_nsim_enable"]:
feature_name = u"output"
else:
feature_name = u"cross-connect"
f"NSIM({feature_name}): delay " \
f"{nsim_attr[u'delay_in_usec']} usecs, " \
f"avg-pkt-size {nsim_attr[u'average_packet_size']}, " \
- f"bandwidth {nsim_attr[u'bandwidth_in_bits_per_second']} " \
+ f"bandwidth {nsim_attr[u'bw_in_bits_per_second']} " \
f"bits/sec, pkt-drop-rate {nsim_attr[u'packets_per_drop']} " \
f"pkts/drop\n"
- if u"error" in program_stderr.lower():
- test_results += f"ERROR DETECTED:\n{program_stderr}"
- raise RuntimeError(test_results)
- if program_stdout:
- bad_test_results = False
- if program == u"vpp_echo" and u"JSON stats" not in program_stdout:
- test_results += u"Invalid test data output!\n"
- bad_test_results = True
- test_results += program_stdout
- if bad_test_results:
- raise RuntimeError(test_results)
- else:
- no_results = True
- test_results += f"\nNo {program} test data retrieved!\n"
- cmd = u"ls -l /tmp/*.log"
- ls_stdout, _ = exec_cmd_no_error(node, cmd, sudo=True)
- test_results += f"{ls_stdout}\n"
-
# TODO: Incorporate show error stats into results analysis
- host = node[u"host"]
test_results += \
- f"\n{role} VPP 'show errors' on host {host}:\n" \
+ f"\n{role} VPP 'show errors' on host {node[u'host']}:\n" \
f"{PapiSocketExecutor.run_cli_cmd(node, u'show error')}\n"
- return no_results, test_results
+ if u"error" in program_stderr.lower():
+ test_results += f"ERROR DETECTED:\n{program_stderr}"
+ return (True, test_results)
+ if not program_stdout:
+ test_results += f"\nNo {program} test data retrieved!\n"
+ ls_stdout, _ = exec_cmd_no_error(node, u"ls -l /tmp/*.log",
+ sudo=True)
+ test_results += f"{ls_stdout}\n"
+ return (True, test_results)
+ if program[u"name"] == u"vpp_echo":
+ 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)
+ 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"]
+ else:
+ test_results += u"Unknown HostStack Test Program!\n" + \
+ program_stdout
+ return (True, program_stdout)
+ return (False, json.dumps(program_json))
@staticmethod
- def no_hoststack_test_program_results(server_no_results, client_no_results):
- """Return True if no HostStack test program output was gathered.
+ def hoststack_test_program_defer_fail(server_defer_fail, client_defer_fail):
+ """Return True if either HostStack test program fail was deferred.
- :param server_no_results: server no results value.
- :param client_no_results: client no results value.
- :type server_no_results: bool
- :type client_no_results: bool
+ :param server_defer_fail: server no results value.
+ :param client_defer_fail: client no results value.
+ :type server_defer_fail: bool
+ :type client_defer_fail: bool
:rtype: bool
"""
- return server_no_results and client_no_results
+ return server_defer_fail and client_defer_fail