X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FVPPUtil.py;h=daeb568bdae8b162d42118e68ef09aafb5e584ae;hp=72325d81697265ce5e9d9a628cb9d1d2ba75d765;hb=b33b1461fe815b1a09267118644538a5b9351c60;hpb=d68951ac245150eeefa6e0f4156e4c1b5c9e9325 diff --git a/resources/libraries/python/VPPUtil.py b/resources/libraries/python/VPPUtil.py index 72325d8169..daeb568bda 100644 --- a/resources/libraries/python/VPPUtil.py +++ b/resources/libraries/python/VPPUtil.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Cisco and/or its affiliates. +# Copyright (c) 2022 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: @@ -18,8 +18,11 @@ from robot.api import logger from resources.libraries.python.Constants import Constants from resources.libraries.python.DUTSetup import DUTSetup from resources.libraries.python.PapiExecutor import PapiSocketExecutor -from resources.libraries.python.ssh import exec_cmd_no_error -from resources.libraries.python.topology import NodeType +from resources.libraries.python.model.ExportResult import ( + export_dut_type_and_version +) +from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd +from resources.libraries.python.topology import Topology, SocketType, NodeType class VPPUtil: @@ -55,13 +58,26 @@ class VPPUtil: exec_cmd_no_error(node, command, timeout=30, sudo=True) @staticmethod - def restart_vpp_service(node): + def restart_vpp_service(node, node_key=None): """Restart VPP service on the specified topology node. + Disconnect possibly connected PAPI executor. + :param node: Topology node. + :param node_key: Topology node key. :type node: dict + :type node_key: str """ + # Containers have a separate lifecycle, but better be safe. + PapiSocketExecutor.disconnect_all_sockets_by_node(node) DUTSetup.restart_service(node, Constants.VPP_UNIT) + if node_key: + Topology.add_new_socket( + node, SocketType.CLI, node_key, Constants.SOCKCLI_PATH) + Topology.add_new_socket( + node, SocketType.PAPI, node_key, Constants.SOCKSVR_PATH) + Topology.add_new_socket( + node, SocketType.STATS, node_key, Constants.SOCKSTAT_PATH) @staticmethod def restart_vpp_service_on_all_duts(nodes): @@ -70,18 +86,27 @@ class VPPUtil: :param nodes: Topology nodes. :type nodes: dict """ - for node in nodes.values(): + for node_key, node in nodes.items(): if node[u"type"] == NodeType.DUT: - VPPUtil.restart_vpp_service(node) + VPPUtil.restart_vpp_service(node, node_key) @staticmethod - def stop_vpp_service(node): + def stop_vpp_service(node, node_key=None): """Stop VPP service on the specified topology node. + Disconnect possibly connected PAPI executor. + :param node: Topology node. + :param node_key: Topology node key. :type node: dict + :type node_key: str """ + # Containers have a separate lifecycle, but better be safe. + PapiSocketExecutor.disconnect_all_sockets_by_node(node) DUTSetup.stop_service(node, Constants.VPP_UNIT) + if node_key: + Topology.del_node_socket_id(node, SocketType.PAPI, node_key) + Topology.del_node_socket_id(node, SocketType.STATS, node_key) @staticmethod def stop_vpp_service_on_all_duts(nodes): @@ -90,9 +115,9 @@ class VPPUtil: :param nodes: Topology nodes. :type nodes: dict """ - for node in nodes.values(): + for node_key, node in nodes.items(): if node[u"type"] == NodeType.DUT: - VPPUtil.stop_vpp_service(node) + VPPUtil.stop_vpp_service(node, node_key) @staticmethod def verify_vpp_installed(node): @@ -101,8 +126,19 @@ class VPPUtil: :param node: Topology node. :type node: dict """ - cmd = u"command -v vpp" - exec_cmd_no_error(node, cmd, message=u"VPP is not installed!") + DUTSetup.verify_program_installed(node, u"vpp") + + @staticmethod + def adjust_privileges(node): + """Adjust privileges to control VPP without sudo. + + :param node: Topology node. + :type node: dict + """ + cmd = u"chmod -R o+rwx /run/vpp" + exec_cmd_no_error( + node, cmd, sudo=True, message=u"Failed to adjust privileges!", + retries=120) @staticmethod def verify_vpp_started(node): @@ -122,19 +158,27 @@ class VPPUtil: node, cmd, sudo=True, message=u"VPP failed to start!", retries=120 ) + # Properly enable cards in case they were disabled. This will be + # followed in https://jira.fd.io/browse/VPP-1934. + cmd = u"for i in $(sudo vppctl sho int | grep Eth | cut -d' ' -f1); do"\ + u" sudo vppctl set int sta $i up; done" + exec_cmd(node, cmd, sudo=False) + @staticmethod def verify_vpp(node): """Verify that VPP is installed and started on the specified topology - node. + node. Adjust privileges so user can connect without sudo. :param node: Topology node. :type node: dict :raises RuntimeError: If VPP service fails to start. """ - VPPUtil.verify_vpp_installed(node) + DUTSetup.verify_program_installed(node, 'vpp') try: # Verify responsiveness of vppctl. VPPUtil.verify_vpp_started(node) + # Adjust privileges. + VPPUtil.adjust_privileges(node) # Verify responsiveness of PAPI. VPPUtil.show_log(node) VPPUtil.vpp_show_version(node) @@ -153,19 +197,32 @@ class VPPUtil: VPPUtil.verify_vpp(node) @staticmethod - def vpp_show_version(node): + def vpp_show_version( + node, remote_vpp_socket=Constants.SOCKSVR_PATH, log=True): """Run "show_version" PAPI command. + Socket is configurable, so VPP inside container can be accessed. + The result is exported to JSON UTI output as "dut-version". + :param node: Node to run command on. + :param remote_vpp_socket: Path to remote socket to target VPP. + :param log: If true, show the result in Robot log. :type node: dict + :type remote_vpp_socket: str + :type log: bool :returns: VPP version. :rtype: str + :raises RuntimeError: If PAPI connection fails. + :raises AssertionError: If PAPI retcode is nonzero. """ cmd = u"show_version" - with PapiSocketExecutor(node) as papi_exec: + with PapiSocketExecutor(node, remote_vpp_socket) as papi_exec: reply = papi_exec.add(cmd).get_reply() - logger.info(f"VPP version: {reply[u'version']}\n") - return f"{reply[u'version']}" + if log: + logger.info(f"VPP version: {reply[u'version']}\n") + version = f"{reply[u'version']}" + export_dut_type_and_version(u"VPP", version) + return version @staticmethod def show_vpp_version_on_all_duts(nodes): @@ -227,7 +284,7 @@ class VPPUtil: for cmd in cmds: try: - PapiSocketExecutor.run_cli_cmd(node, cmd) + PapiSocketExecutor.run_cli_cmd_on_all_sockets(node, cmd) except AssertionError: if fail_on_error: raise @@ -253,7 +310,13 @@ class VPPUtil: :param node: Topology node. :type node: dict """ - PapiSocketExecutor.run_cli_cmd(node, "elog trace api cli barrier") + try: + PapiSocketExecutor.run_cli_cmd_on_all_sockets( + node, u"event-logger trace api cli barrier") + except AssertionError: + # Perhaps an older VPP build is tested. + PapiSocketExecutor.run_cli_cmd_on_all_sockets( + node, u"elog trace api cli barrier") @staticmethod def vpp_enable_elog_traces_on_all_duts(nodes): @@ -273,7 +336,8 @@ class VPPUtil: :param node: Topology node. :type node: dict """ - PapiSocketExecutor.run_cli_cmd(node, u"show event-logger") + PapiSocketExecutor.run_cli_cmd_on_all_sockets( + node, u"show event-logger") @staticmethod def show_event_logger_on_all_duts(nodes): @@ -319,15 +383,30 @@ class VPPUtil: with PapiSocketExecutor(node) as papi_exec: reply = papi_exec.add(cmd).get_reply() - threads_data = list() - for thread in reply[u"thread_data"]: - thread_data = list() - for item in thread: - if isinstance(item, str): - item = item.rstrip('\x00') - thread_data.append(item) - threads_data.append(thread_data) - + threads_data = reply[u"thread_data"] logger.trace(f"show threads:\n{threads_data}") return threads_data + + @staticmethod + def vpp_add_graph_node_next(node, graph_node_name, graph_next_name): + """Set the next node for a given node. + + :param node: Node to run command on. + :param graph_node_name: Graph node to add the next node on. + :param graph_next_name: Graph node to add as the next node. + :type node: dict + :type graph_node_name: str + :type graph_next_name: str + :returns: The index of the next graph node. + :rtype: int + """ + cmd = u"add_node_next" + args = dict( + node_name=graph_node_name, + next_name=graph_next_name + ) + with PapiSocketExecutor(node) as papi_exec: + reply = papi_exec.add(cmd, **args).get_reply() + + return reply[u"next_index"]