1840b1e01SJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause 2840b1e01SJuraj Linkeš# Copyright(c) 2023 University of New Hampshire 36ef07151SJuraj Linkeš# Copyright(c) 2023 PANTHEON.tech s.r.o. 461d5bc9bSLuca Vizzarro# Copyright(c) 2024 Arm Limited 56ef07151SJuraj Linkeš 66ef07151SJuraj Linkeš"""Testpmd interactive shell. 76ef07151SJuraj Linkeš 86ef07151SJuraj LinkešTypical usage example in a TestSuite:: 96ef07151SJuraj Linkeš 10*bfad0948SLuca Vizzarro testpmd_shell = TestPmdShell(self.sut_node) 116ef07151SJuraj Linkeš devices = testpmd_shell.get_devices() 126ef07151SJuraj Linkeš for device in devices: 136ef07151SJuraj Linkeš print(device) 146ef07151SJuraj Linkeš testpmd_shell.close() 156ef07151SJuraj Linkeš""" 16840b1e01SJuraj Linkeš 1761d5bc9bSLuca Vizzarroimport re 18369d34b8SJeremy Spewockimport time 1961d5bc9bSLuca Vizzarrofrom dataclasses import dataclass, field 2061d5bc9bSLuca Vizzarrofrom enum import Flag, auto 21840b1e01SJuraj Linkešfrom pathlib import PurePath 22*bfad0948SLuca Vizzarrofrom typing import ClassVar 23840b1e01SJuraj Linkeš 2461d5bc9bSLuca Vizzarrofrom typing_extensions import Self 2561d5bc9bSLuca Vizzarro 26369d34b8SJeremy Spewockfrom framework.exception import InteractiveCommandExecutionError 27fc0f7dc4SLuca Vizzarrofrom framework.params.testpmd import SimpleForwardingModes, TestPmdParams 2861d5bc9bSLuca Vizzarrofrom framework.parser import ParserFn, TextParser 29*bfad0948SLuca Vizzarrofrom framework.remote_session.dpdk_shell import DPDKShell 30369d34b8SJeremy Spewockfrom framework.settings import SETTINGS 31*bfad0948SLuca Vizzarrofrom framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList 32*bfad0948SLuca Vizzarrofrom framework.testbed_model.sut_node import SutNode 33369d34b8SJeremy Spewockfrom framework.utils import StrEnum 34369d34b8SJeremy Spewock 35840b1e01SJuraj Linkeš 363e967643SJuraj Linkešclass TestPmdDevice: 376ef07151SJuraj Linkeš """The data of a device that testpmd can recognize. 386ef07151SJuraj Linkeš 396ef07151SJuraj Linkeš Attributes: 406ef07151SJuraj Linkeš pci_address: The PCI address of the device. 416ef07151SJuraj Linkeš """ 426ef07151SJuraj Linkeš 43840b1e01SJuraj Linkeš pci_address: str 44840b1e01SJuraj Linkeš 45840b1e01SJuraj Linkeš def __init__(self, pci_address_line: str): 466ef07151SJuraj Linkeš """Initialize the device from the testpmd output line string. 476ef07151SJuraj Linkeš 486ef07151SJuraj Linkeš Args: 496ef07151SJuraj Linkeš pci_address_line: A line of testpmd output that contains a device. 506ef07151SJuraj Linkeš """ 51840b1e01SJuraj Linkeš self.pci_address = pci_address_line.strip().split(": ")[1].strip() 52840b1e01SJuraj Linkeš 53840b1e01SJuraj Linkeš def __str__(self) -> str: 546ef07151SJuraj Linkeš """The PCI address captures what the device is.""" 55840b1e01SJuraj Linkeš return self.pci_address 56840b1e01SJuraj Linkeš 57840b1e01SJuraj Linkeš 5861d5bc9bSLuca Vizzarroclass VLANOffloadFlag(Flag): 5961d5bc9bSLuca Vizzarro """Flag representing the VLAN offload settings of a NIC port.""" 6061d5bc9bSLuca Vizzarro 6161d5bc9bSLuca Vizzarro #: 6261d5bc9bSLuca Vizzarro STRIP = auto() 6361d5bc9bSLuca Vizzarro #: 6461d5bc9bSLuca Vizzarro FILTER = auto() 6561d5bc9bSLuca Vizzarro #: 6661d5bc9bSLuca Vizzarro EXTEND = auto() 6761d5bc9bSLuca Vizzarro #: 6861d5bc9bSLuca Vizzarro QINQ_STRIP = auto() 6961d5bc9bSLuca Vizzarro 7061d5bc9bSLuca Vizzarro @classmethod 7161d5bc9bSLuca Vizzarro def from_str_dict(cls, d): 7261d5bc9bSLuca Vizzarro """Makes an instance from a dict containing the flag member names with an "on" value. 7361d5bc9bSLuca Vizzarro 7461d5bc9bSLuca Vizzarro Args: 7561d5bc9bSLuca Vizzarro d: A dictionary containing the flag members as keys and any string value. 7661d5bc9bSLuca Vizzarro 7761d5bc9bSLuca Vizzarro Returns: 7861d5bc9bSLuca Vizzarro A new instance of the flag. 7961d5bc9bSLuca Vizzarro """ 8061d5bc9bSLuca Vizzarro flag = cls(0) 8161d5bc9bSLuca Vizzarro for name in cls.__members__: 8261d5bc9bSLuca Vizzarro if d.get(name) == "on": 8361d5bc9bSLuca Vizzarro flag |= cls[name] 8461d5bc9bSLuca Vizzarro return flag 8561d5bc9bSLuca Vizzarro 8661d5bc9bSLuca Vizzarro @classmethod 8761d5bc9bSLuca Vizzarro def make_parser(cls) -> ParserFn: 8861d5bc9bSLuca Vizzarro """Makes a parser function. 8961d5bc9bSLuca Vizzarro 9061d5bc9bSLuca Vizzarro Returns: 9161d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 9261d5bc9bSLuca Vizzarro parser function that makes an instance of this flag from text. 9361d5bc9bSLuca Vizzarro """ 9461d5bc9bSLuca Vizzarro return TextParser.wrap( 9561d5bc9bSLuca Vizzarro TextParser.find( 9661d5bc9bSLuca Vizzarro r"VLAN offload:\s+" 9761d5bc9bSLuca Vizzarro r"strip (?P<STRIP>on|off), " 9861d5bc9bSLuca Vizzarro r"filter (?P<FILTER>on|off), " 9961d5bc9bSLuca Vizzarro r"extend (?P<EXTEND>on|off), " 10061d5bc9bSLuca Vizzarro r"qinq strip (?P<QINQ_STRIP>on|off)$", 10161d5bc9bSLuca Vizzarro re.MULTILINE, 10261d5bc9bSLuca Vizzarro named=True, 10361d5bc9bSLuca Vizzarro ), 10461d5bc9bSLuca Vizzarro cls.from_str_dict, 10561d5bc9bSLuca Vizzarro ) 10661d5bc9bSLuca Vizzarro 10761d5bc9bSLuca Vizzarro 10861d5bc9bSLuca Vizzarroclass RSSOffloadTypesFlag(Flag): 10961d5bc9bSLuca Vizzarro """Flag representing the RSS offload flow types supported by the NIC port.""" 11061d5bc9bSLuca Vizzarro 11161d5bc9bSLuca Vizzarro #: 11261d5bc9bSLuca Vizzarro ipv4 = auto() 11361d5bc9bSLuca Vizzarro #: 11461d5bc9bSLuca Vizzarro ipv4_frag = auto() 11561d5bc9bSLuca Vizzarro #: 11661d5bc9bSLuca Vizzarro ipv4_tcp = auto() 11761d5bc9bSLuca Vizzarro #: 11861d5bc9bSLuca Vizzarro ipv4_udp = auto() 11961d5bc9bSLuca Vizzarro #: 12061d5bc9bSLuca Vizzarro ipv4_sctp = auto() 12161d5bc9bSLuca Vizzarro #: 12261d5bc9bSLuca Vizzarro ipv4_other = auto() 12361d5bc9bSLuca Vizzarro #: 12461d5bc9bSLuca Vizzarro ipv6 = auto() 12561d5bc9bSLuca Vizzarro #: 12661d5bc9bSLuca Vizzarro ipv6_frag = auto() 12761d5bc9bSLuca Vizzarro #: 12861d5bc9bSLuca Vizzarro ipv6_tcp = auto() 12961d5bc9bSLuca Vizzarro #: 13061d5bc9bSLuca Vizzarro ipv6_udp = auto() 13161d5bc9bSLuca Vizzarro #: 13261d5bc9bSLuca Vizzarro ipv6_sctp = auto() 13361d5bc9bSLuca Vizzarro #: 13461d5bc9bSLuca Vizzarro ipv6_other = auto() 13561d5bc9bSLuca Vizzarro #: 13661d5bc9bSLuca Vizzarro l2_payload = auto() 13761d5bc9bSLuca Vizzarro #: 13861d5bc9bSLuca Vizzarro ipv6_ex = auto() 13961d5bc9bSLuca Vizzarro #: 14061d5bc9bSLuca Vizzarro ipv6_tcp_ex = auto() 14161d5bc9bSLuca Vizzarro #: 14261d5bc9bSLuca Vizzarro ipv6_udp_ex = auto() 14361d5bc9bSLuca Vizzarro #: 14461d5bc9bSLuca Vizzarro port = auto() 14561d5bc9bSLuca Vizzarro #: 14661d5bc9bSLuca Vizzarro vxlan = auto() 14761d5bc9bSLuca Vizzarro #: 14861d5bc9bSLuca Vizzarro geneve = auto() 14961d5bc9bSLuca Vizzarro #: 15061d5bc9bSLuca Vizzarro nvgre = auto() 15161d5bc9bSLuca Vizzarro #: 15261d5bc9bSLuca Vizzarro user_defined_22 = auto() 15361d5bc9bSLuca Vizzarro #: 15461d5bc9bSLuca Vizzarro gtpu = auto() 15561d5bc9bSLuca Vizzarro #: 15661d5bc9bSLuca Vizzarro eth = auto() 15761d5bc9bSLuca Vizzarro #: 15861d5bc9bSLuca Vizzarro s_vlan = auto() 15961d5bc9bSLuca Vizzarro #: 16061d5bc9bSLuca Vizzarro c_vlan = auto() 16161d5bc9bSLuca Vizzarro #: 16261d5bc9bSLuca Vizzarro esp = auto() 16361d5bc9bSLuca Vizzarro #: 16461d5bc9bSLuca Vizzarro ah = auto() 16561d5bc9bSLuca Vizzarro #: 16661d5bc9bSLuca Vizzarro l2tpv3 = auto() 16761d5bc9bSLuca Vizzarro #: 16861d5bc9bSLuca Vizzarro pfcp = auto() 16961d5bc9bSLuca Vizzarro #: 17061d5bc9bSLuca Vizzarro pppoe = auto() 17161d5bc9bSLuca Vizzarro #: 17261d5bc9bSLuca Vizzarro ecpri = auto() 17361d5bc9bSLuca Vizzarro #: 17461d5bc9bSLuca Vizzarro mpls = auto() 17561d5bc9bSLuca Vizzarro #: 17661d5bc9bSLuca Vizzarro ipv4_chksum = auto() 17761d5bc9bSLuca Vizzarro #: 17861d5bc9bSLuca Vizzarro l4_chksum = auto() 17961d5bc9bSLuca Vizzarro #: 18061d5bc9bSLuca Vizzarro l2tpv2 = auto() 18161d5bc9bSLuca Vizzarro #: 18261d5bc9bSLuca Vizzarro ipv6_flow_label = auto() 18361d5bc9bSLuca Vizzarro #: 18461d5bc9bSLuca Vizzarro user_defined_38 = auto() 18561d5bc9bSLuca Vizzarro #: 18661d5bc9bSLuca Vizzarro user_defined_39 = auto() 18761d5bc9bSLuca Vizzarro #: 18861d5bc9bSLuca Vizzarro user_defined_40 = auto() 18961d5bc9bSLuca Vizzarro #: 19061d5bc9bSLuca Vizzarro user_defined_41 = auto() 19161d5bc9bSLuca Vizzarro #: 19261d5bc9bSLuca Vizzarro user_defined_42 = auto() 19361d5bc9bSLuca Vizzarro #: 19461d5bc9bSLuca Vizzarro user_defined_43 = auto() 19561d5bc9bSLuca Vizzarro #: 19661d5bc9bSLuca Vizzarro user_defined_44 = auto() 19761d5bc9bSLuca Vizzarro #: 19861d5bc9bSLuca Vizzarro user_defined_45 = auto() 19961d5bc9bSLuca Vizzarro #: 20061d5bc9bSLuca Vizzarro user_defined_46 = auto() 20161d5bc9bSLuca Vizzarro #: 20261d5bc9bSLuca Vizzarro user_defined_47 = auto() 20361d5bc9bSLuca Vizzarro #: 20461d5bc9bSLuca Vizzarro user_defined_48 = auto() 20561d5bc9bSLuca Vizzarro #: 20661d5bc9bSLuca Vizzarro user_defined_49 = auto() 20761d5bc9bSLuca Vizzarro #: 20861d5bc9bSLuca Vizzarro user_defined_50 = auto() 20961d5bc9bSLuca Vizzarro #: 21061d5bc9bSLuca Vizzarro user_defined_51 = auto() 21161d5bc9bSLuca Vizzarro #: 21261d5bc9bSLuca Vizzarro l3_pre96 = auto() 21361d5bc9bSLuca Vizzarro #: 21461d5bc9bSLuca Vizzarro l3_pre64 = auto() 21561d5bc9bSLuca Vizzarro #: 21661d5bc9bSLuca Vizzarro l3_pre56 = auto() 21761d5bc9bSLuca Vizzarro #: 21861d5bc9bSLuca Vizzarro l3_pre48 = auto() 21961d5bc9bSLuca Vizzarro #: 22061d5bc9bSLuca Vizzarro l3_pre40 = auto() 22161d5bc9bSLuca Vizzarro #: 22261d5bc9bSLuca Vizzarro l3_pre32 = auto() 22361d5bc9bSLuca Vizzarro #: 22461d5bc9bSLuca Vizzarro l2_dst_only = auto() 22561d5bc9bSLuca Vizzarro #: 22661d5bc9bSLuca Vizzarro l2_src_only = auto() 22761d5bc9bSLuca Vizzarro #: 22861d5bc9bSLuca Vizzarro l4_dst_only = auto() 22961d5bc9bSLuca Vizzarro #: 23061d5bc9bSLuca Vizzarro l4_src_only = auto() 23161d5bc9bSLuca Vizzarro #: 23261d5bc9bSLuca Vizzarro l3_dst_only = auto() 23361d5bc9bSLuca Vizzarro #: 23461d5bc9bSLuca Vizzarro l3_src_only = auto() 23561d5bc9bSLuca Vizzarro 23661d5bc9bSLuca Vizzarro #: 23761d5bc9bSLuca Vizzarro ip = ipv4 | ipv4_frag | ipv4_other | ipv6 | ipv6_frag | ipv6_other | ipv6_ex 23861d5bc9bSLuca Vizzarro #: 23961d5bc9bSLuca Vizzarro udp = ipv4_udp | ipv6_udp | ipv6_udp_ex 24061d5bc9bSLuca Vizzarro #: 24161d5bc9bSLuca Vizzarro tcp = ipv4_tcp | ipv6_tcp | ipv6_tcp_ex 24261d5bc9bSLuca Vizzarro #: 24361d5bc9bSLuca Vizzarro sctp = ipv4_sctp | ipv6_sctp 24461d5bc9bSLuca Vizzarro #: 24561d5bc9bSLuca Vizzarro tunnel = vxlan | geneve | nvgre 24661d5bc9bSLuca Vizzarro #: 24761d5bc9bSLuca Vizzarro vlan = s_vlan | c_vlan 24861d5bc9bSLuca Vizzarro #: 24961d5bc9bSLuca Vizzarro all = ( 25061d5bc9bSLuca Vizzarro eth 25161d5bc9bSLuca Vizzarro | vlan 25261d5bc9bSLuca Vizzarro | ip 25361d5bc9bSLuca Vizzarro | tcp 25461d5bc9bSLuca Vizzarro | udp 25561d5bc9bSLuca Vizzarro | sctp 25661d5bc9bSLuca Vizzarro | l2_payload 25761d5bc9bSLuca Vizzarro | l2tpv3 25861d5bc9bSLuca Vizzarro | esp 25961d5bc9bSLuca Vizzarro | ah 26061d5bc9bSLuca Vizzarro | pfcp 26161d5bc9bSLuca Vizzarro | gtpu 26261d5bc9bSLuca Vizzarro | ecpri 26361d5bc9bSLuca Vizzarro | mpls 26461d5bc9bSLuca Vizzarro | l2tpv2 26561d5bc9bSLuca Vizzarro ) 26661d5bc9bSLuca Vizzarro 26761d5bc9bSLuca Vizzarro @classmethod 26861d5bc9bSLuca Vizzarro def from_list_string(cls, names: str) -> Self: 26961d5bc9bSLuca Vizzarro """Makes a flag from a whitespace-separated list of names. 27061d5bc9bSLuca Vizzarro 27161d5bc9bSLuca Vizzarro Args: 27261d5bc9bSLuca Vizzarro names: a whitespace-separated list containing the members of this flag. 27361d5bc9bSLuca Vizzarro 27461d5bc9bSLuca Vizzarro Returns: 27561d5bc9bSLuca Vizzarro An instance of this flag. 27661d5bc9bSLuca Vizzarro """ 27761d5bc9bSLuca Vizzarro flag = cls(0) 27861d5bc9bSLuca Vizzarro for name in names.split(): 27961d5bc9bSLuca Vizzarro flag |= cls.from_str(name) 28061d5bc9bSLuca Vizzarro return flag 28161d5bc9bSLuca Vizzarro 28261d5bc9bSLuca Vizzarro @classmethod 28361d5bc9bSLuca Vizzarro def from_str(cls, name: str) -> Self: 28461d5bc9bSLuca Vizzarro """Makes a flag matching the supplied name. 28561d5bc9bSLuca Vizzarro 28661d5bc9bSLuca Vizzarro Args: 28761d5bc9bSLuca Vizzarro name: a valid member of this flag in text 28861d5bc9bSLuca Vizzarro Returns: 28961d5bc9bSLuca Vizzarro An instance of this flag. 29061d5bc9bSLuca Vizzarro """ 29161d5bc9bSLuca Vizzarro member_name = name.strip().replace("-", "_") 29261d5bc9bSLuca Vizzarro return cls[member_name] 29361d5bc9bSLuca Vizzarro 29461d5bc9bSLuca Vizzarro @classmethod 29561d5bc9bSLuca Vizzarro def make_parser(cls) -> ParserFn: 29661d5bc9bSLuca Vizzarro """Makes a parser function. 29761d5bc9bSLuca Vizzarro 29861d5bc9bSLuca Vizzarro Returns: 29961d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 30061d5bc9bSLuca Vizzarro parser function that makes an instance of this flag from text. 30161d5bc9bSLuca Vizzarro """ 30261d5bc9bSLuca Vizzarro return TextParser.wrap( 30361d5bc9bSLuca Vizzarro TextParser.find(r"Supported RSS offload flow types:((?:\r?\n? \S+)+)", re.MULTILINE), 30461d5bc9bSLuca Vizzarro RSSOffloadTypesFlag.from_list_string, 30561d5bc9bSLuca Vizzarro ) 30661d5bc9bSLuca Vizzarro 30761d5bc9bSLuca Vizzarro 30861d5bc9bSLuca Vizzarroclass DeviceCapabilitiesFlag(Flag): 30961d5bc9bSLuca Vizzarro """Flag representing the device capabilities.""" 31061d5bc9bSLuca Vizzarro 31161d5bc9bSLuca Vizzarro #: Device supports Rx queue setup after device started. 31261d5bc9bSLuca Vizzarro RUNTIME_RX_QUEUE_SETUP = auto() 31361d5bc9bSLuca Vizzarro #: Device supports Tx queue setup after device started. 31461d5bc9bSLuca Vizzarro RUNTIME_TX_QUEUE_SETUP = auto() 31561d5bc9bSLuca Vizzarro #: Device supports shared Rx queue among ports within Rx domain and switch domain. 31661d5bc9bSLuca Vizzarro RXQ_SHARE = auto() 31761d5bc9bSLuca Vizzarro #: Device supports keeping flow rules across restart. 31861d5bc9bSLuca Vizzarro FLOW_RULE_KEEP = auto() 31961d5bc9bSLuca Vizzarro #: Device supports keeping shared flow objects across restart. 32061d5bc9bSLuca Vizzarro FLOW_SHARED_OBJECT_KEEP = auto() 32161d5bc9bSLuca Vizzarro 32261d5bc9bSLuca Vizzarro @classmethod 32361d5bc9bSLuca Vizzarro def make_parser(cls) -> ParserFn: 32461d5bc9bSLuca Vizzarro """Makes a parser function. 32561d5bc9bSLuca Vizzarro 32661d5bc9bSLuca Vizzarro Returns: 32761d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 32861d5bc9bSLuca Vizzarro parser function that makes an instance of this flag from text. 32961d5bc9bSLuca Vizzarro """ 33061d5bc9bSLuca Vizzarro return TextParser.wrap( 33161d5bc9bSLuca Vizzarro TextParser.find_int(r"Device capabilities: (0x[A-Fa-f\d]+)"), 33261d5bc9bSLuca Vizzarro cls, 33361d5bc9bSLuca Vizzarro ) 33461d5bc9bSLuca Vizzarro 33561d5bc9bSLuca Vizzarro 33661d5bc9bSLuca Vizzarroclass DeviceErrorHandlingMode(StrEnum): 33761d5bc9bSLuca Vizzarro """Enum representing the device error handling mode.""" 33861d5bc9bSLuca Vizzarro 33961d5bc9bSLuca Vizzarro #: 34061d5bc9bSLuca Vizzarro none = auto() 34161d5bc9bSLuca Vizzarro #: 34261d5bc9bSLuca Vizzarro passive = auto() 34361d5bc9bSLuca Vizzarro #: 34461d5bc9bSLuca Vizzarro proactive = auto() 34561d5bc9bSLuca Vizzarro #: 34661d5bc9bSLuca Vizzarro unknown = auto() 34761d5bc9bSLuca Vizzarro 34861d5bc9bSLuca Vizzarro @classmethod 34961d5bc9bSLuca Vizzarro def make_parser(cls) -> ParserFn: 35061d5bc9bSLuca Vizzarro """Makes a parser function. 35161d5bc9bSLuca Vizzarro 35261d5bc9bSLuca Vizzarro Returns: 35361d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 35461d5bc9bSLuca Vizzarro parser function that makes an instance of this enum from text. 35561d5bc9bSLuca Vizzarro """ 35661d5bc9bSLuca Vizzarro return TextParser.wrap(TextParser.find(r"Device error handling mode: (\w+)"), cls) 35761d5bc9bSLuca Vizzarro 35861d5bc9bSLuca Vizzarro 35961d5bc9bSLuca Vizzarrodef make_device_private_info_parser() -> ParserFn: 36061d5bc9bSLuca Vizzarro """Device private information parser. 36161d5bc9bSLuca Vizzarro 36261d5bc9bSLuca Vizzarro Ensures that we are not parsing invalid device private info output. 36361d5bc9bSLuca Vizzarro 36461d5bc9bSLuca Vizzarro Returns: 36561d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a parser 36661d5bc9bSLuca Vizzarro function that parses the device private info from the TestPmd port info output. 36761d5bc9bSLuca Vizzarro """ 36861d5bc9bSLuca Vizzarro 36961d5bc9bSLuca Vizzarro def _validate(info: str): 37061d5bc9bSLuca Vizzarro info = info.strip() 37161d5bc9bSLuca Vizzarro if info == "none" or info.startswith("Invalid file") or info.startswith("Failed to dump"): 37261d5bc9bSLuca Vizzarro return None 37361d5bc9bSLuca Vizzarro return info 37461d5bc9bSLuca Vizzarro 37561d5bc9bSLuca Vizzarro return TextParser.wrap(TextParser.find(r"Device private info:\s+([\s\S]+)"), _validate) 37661d5bc9bSLuca Vizzarro 37761d5bc9bSLuca Vizzarro 37861d5bc9bSLuca Vizzarro@dataclass 37961d5bc9bSLuca Vizzarroclass TestPmdPort(TextParser): 38061d5bc9bSLuca Vizzarro """Dataclass representing the result of testpmd's ``show port info`` command.""" 38161d5bc9bSLuca Vizzarro 38261d5bc9bSLuca Vizzarro #: 38361d5bc9bSLuca Vizzarro id: int = field(metadata=TextParser.find_int(r"Infos for port (\d+)\b")) 38461d5bc9bSLuca Vizzarro #: 38561d5bc9bSLuca Vizzarro device_name: str = field(metadata=TextParser.find(r"Device name: ([^\r\n]+)")) 38661d5bc9bSLuca Vizzarro #: 38761d5bc9bSLuca Vizzarro driver_name: str = field(metadata=TextParser.find(r"Driver name: ([^\r\n]+)")) 38861d5bc9bSLuca Vizzarro #: 38961d5bc9bSLuca Vizzarro socket_id: int = field(metadata=TextParser.find_int(r"Connect to socket: (\d+)")) 39061d5bc9bSLuca Vizzarro #: 39161d5bc9bSLuca Vizzarro is_link_up: bool = field(metadata=TextParser.find("Link status: up")) 39261d5bc9bSLuca Vizzarro #: 39361d5bc9bSLuca Vizzarro link_speed: str = field(metadata=TextParser.find(r"Link speed: ([^\r\n]+)")) 39461d5bc9bSLuca Vizzarro #: 39561d5bc9bSLuca Vizzarro is_link_full_duplex: bool = field(metadata=TextParser.find("Link duplex: full-duplex")) 39661d5bc9bSLuca Vizzarro #: 39761d5bc9bSLuca Vizzarro is_link_autonegotiated: bool = field(metadata=TextParser.find("Autoneg status: On")) 39861d5bc9bSLuca Vizzarro #: 39961d5bc9bSLuca Vizzarro is_promiscuous_mode_enabled: bool = field(metadata=TextParser.find("Promiscuous mode: enabled")) 40061d5bc9bSLuca Vizzarro #: 40161d5bc9bSLuca Vizzarro is_allmulticast_mode_enabled: bool = field( 40261d5bc9bSLuca Vizzarro metadata=TextParser.find("Allmulticast mode: enabled") 40361d5bc9bSLuca Vizzarro ) 40461d5bc9bSLuca Vizzarro #: Maximum number of MAC addresses 40561d5bc9bSLuca Vizzarro max_mac_addresses_num: int = field( 40661d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Maximum number of MAC addresses: (\d+)") 40761d5bc9bSLuca Vizzarro ) 40861d5bc9bSLuca Vizzarro #: Maximum configurable length of RX packet 40961d5bc9bSLuca Vizzarro max_hash_mac_addresses_num: int = field( 41061d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Maximum number of MAC addresses of hash filtering: (\d+)") 41161d5bc9bSLuca Vizzarro ) 41261d5bc9bSLuca Vizzarro #: Minimum size of RX buffer 41361d5bc9bSLuca Vizzarro min_rx_bufsize: int = field(metadata=TextParser.find_int(r"Minimum size of RX buffer: (\d+)")) 41461d5bc9bSLuca Vizzarro #: Maximum configurable length of RX packet 41561d5bc9bSLuca Vizzarro max_rx_packet_length: int = field( 41661d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Maximum configurable length of RX packet: (\d+)") 41761d5bc9bSLuca Vizzarro ) 41861d5bc9bSLuca Vizzarro #: Maximum configurable size of LRO aggregated packet 41961d5bc9bSLuca Vizzarro max_lro_packet_size: int = field( 42061d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Maximum configurable size of LRO aggregated packet: (\d+)") 42161d5bc9bSLuca Vizzarro ) 42261d5bc9bSLuca Vizzarro 42361d5bc9bSLuca Vizzarro #: Current number of RX queues 42461d5bc9bSLuca Vizzarro rx_queues_num: int = field(metadata=TextParser.find_int(r"Current number of RX queues: (\d+)")) 42561d5bc9bSLuca Vizzarro #: Max possible RX queues 42661d5bc9bSLuca Vizzarro max_rx_queues_num: int = field(metadata=TextParser.find_int(r"Max possible RX queues: (\d+)")) 42761d5bc9bSLuca Vizzarro #: Max possible number of RXDs per queue 42861d5bc9bSLuca Vizzarro max_queue_rxd_num: int = field( 42961d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Max possible number of RXDs per queue: (\d+)") 43061d5bc9bSLuca Vizzarro ) 43161d5bc9bSLuca Vizzarro #: Min possible number of RXDs per queue 43261d5bc9bSLuca Vizzarro min_queue_rxd_num: int = field( 43361d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Min possible number of RXDs per queue: (\d+)") 43461d5bc9bSLuca Vizzarro ) 43561d5bc9bSLuca Vizzarro #: RXDs number alignment 43661d5bc9bSLuca Vizzarro rxd_alignment_num: int = field(metadata=TextParser.find_int(r"RXDs number alignment: (\d+)")) 43761d5bc9bSLuca Vizzarro 43861d5bc9bSLuca Vizzarro #: Current number of TX queues 43961d5bc9bSLuca Vizzarro tx_queues_num: int = field(metadata=TextParser.find_int(r"Current number of TX queues: (\d+)")) 44061d5bc9bSLuca Vizzarro #: Max possible TX queues 44161d5bc9bSLuca Vizzarro max_tx_queues_num: int = field(metadata=TextParser.find_int(r"Max possible TX queues: (\d+)")) 44261d5bc9bSLuca Vizzarro #: Max possible number of TXDs per queue 44361d5bc9bSLuca Vizzarro max_queue_txd_num: int = field( 44461d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Max possible number of TXDs per queue: (\d+)") 44561d5bc9bSLuca Vizzarro ) 44661d5bc9bSLuca Vizzarro #: Min possible number of TXDs per queue 44761d5bc9bSLuca Vizzarro min_queue_txd_num: int = field( 44861d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Min possible number of TXDs per queue: (\d+)") 44961d5bc9bSLuca Vizzarro ) 45061d5bc9bSLuca Vizzarro #: TXDs number alignment 45161d5bc9bSLuca Vizzarro txd_alignment_num: int = field(metadata=TextParser.find_int(r"TXDs number alignment: (\d+)")) 45261d5bc9bSLuca Vizzarro #: Max segment number per packet 45361d5bc9bSLuca Vizzarro max_packet_segment_num: int = field( 45461d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Max segment number per packet: (\d+)") 45561d5bc9bSLuca Vizzarro ) 45661d5bc9bSLuca Vizzarro #: Max segment number per MTU/TSO 45761d5bc9bSLuca Vizzarro max_mtu_segment_num: int = field( 45861d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Max segment number per MTU\/TSO: (\d+)") 45961d5bc9bSLuca Vizzarro ) 46061d5bc9bSLuca Vizzarro 46161d5bc9bSLuca Vizzarro #: 46261d5bc9bSLuca Vizzarro device_capabilities: DeviceCapabilitiesFlag = field( 46361d5bc9bSLuca Vizzarro metadata=DeviceCapabilitiesFlag.make_parser(), 46461d5bc9bSLuca Vizzarro ) 46561d5bc9bSLuca Vizzarro #: 46661d5bc9bSLuca Vizzarro device_error_handling_mode: DeviceErrorHandlingMode = field( 46761d5bc9bSLuca Vizzarro metadata=DeviceErrorHandlingMode.make_parser() 46861d5bc9bSLuca Vizzarro ) 46961d5bc9bSLuca Vizzarro #: 47061d5bc9bSLuca Vizzarro device_private_info: str | None = field( 47161d5bc9bSLuca Vizzarro default=None, 47261d5bc9bSLuca Vizzarro metadata=make_device_private_info_parser(), 47361d5bc9bSLuca Vizzarro ) 47461d5bc9bSLuca Vizzarro 47561d5bc9bSLuca Vizzarro #: 47661d5bc9bSLuca Vizzarro hash_key_size: int | None = field( 47761d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Hash key size in bytes: (\d+)") 47861d5bc9bSLuca Vizzarro ) 47961d5bc9bSLuca Vizzarro #: 48061d5bc9bSLuca Vizzarro redirection_table_size: int | None = field( 48161d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Redirection table size: (\d+)") 48261d5bc9bSLuca Vizzarro ) 48361d5bc9bSLuca Vizzarro #: 48461d5bc9bSLuca Vizzarro supported_rss_offload_flow_types: RSSOffloadTypesFlag = field( 48561d5bc9bSLuca Vizzarro default=RSSOffloadTypesFlag(0), metadata=RSSOffloadTypesFlag.make_parser() 48661d5bc9bSLuca Vizzarro ) 48761d5bc9bSLuca Vizzarro 48861d5bc9bSLuca Vizzarro #: 48961d5bc9bSLuca Vizzarro mac_address: str | None = field( 49061d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find(r"MAC address: ([A-Fa-f0-9:]+)") 49161d5bc9bSLuca Vizzarro ) 49261d5bc9bSLuca Vizzarro #: 49361d5bc9bSLuca Vizzarro fw_version: str | None = field( 49461d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find(r"Firmware-version: ([^\r\n]+)") 49561d5bc9bSLuca Vizzarro ) 49661d5bc9bSLuca Vizzarro #: 49761d5bc9bSLuca Vizzarro dev_args: str | None = field(default=None, metadata=TextParser.find(r"Devargs: ([^\r\n]+)")) 49861d5bc9bSLuca Vizzarro #: Socket id of the memory allocation 49961d5bc9bSLuca Vizzarro mem_alloc_socket_id: int | None = field( 50061d5bc9bSLuca Vizzarro default=None, 50161d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"memory allocation on the socket: (\d+)"), 50261d5bc9bSLuca Vizzarro ) 50361d5bc9bSLuca Vizzarro #: 50461d5bc9bSLuca Vizzarro mtu: int | None = field(default=None, metadata=TextParser.find_int(r"MTU: (\d+)")) 50561d5bc9bSLuca Vizzarro 50661d5bc9bSLuca Vizzarro #: 50761d5bc9bSLuca Vizzarro vlan_offload: VLANOffloadFlag | None = field( 50861d5bc9bSLuca Vizzarro default=None, 50961d5bc9bSLuca Vizzarro metadata=VLANOffloadFlag.make_parser(), 51061d5bc9bSLuca Vizzarro ) 51161d5bc9bSLuca Vizzarro 51261d5bc9bSLuca Vizzarro #: Maximum size of RX buffer 51361d5bc9bSLuca Vizzarro max_rx_bufsize: int | None = field( 51461d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Maximum size of RX buffer: (\d+)") 51561d5bc9bSLuca Vizzarro ) 51661d5bc9bSLuca Vizzarro #: Maximum number of VFs 51761d5bc9bSLuca Vizzarro max_vfs_num: int | None = field( 51861d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Maximum number of VFs: (\d+)") 51961d5bc9bSLuca Vizzarro ) 52061d5bc9bSLuca Vizzarro #: Maximum number of VMDq pools 52161d5bc9bSLuca Vizzarro max_vmdq_pools_num: int | None = field( 52261d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Maximum number of VMDq pools: (\d+)") 52361d5bc9bSLuca Vizzarro ) 52461d5bc9bSLuca Vizzarro 52561d5bc9bSLuca Vizzarro #: 52661d5bc9bSLuca Vizzarro switch_name: str | None = field( 52761d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find(r"Switch name: ([\r\n]+)") 52861d5bc9bSLuca Vizzarro ) 52961d5bc9bSLuca Vizzarro #: 53061d5bc9bSLuca Vizzarro switch_domain_id: int | None = field( 53161d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Switch domain Id: (\d+)") 53261d5bc9bSLuca Vizzarro ) 53361d5bc9bSLuca Vizzarro #: 53461d5bc9bSLuca Vizzarro switch_port_id: int | None = field( 53561d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Switch Port Id: (\d+)") 53661d5bc9bSLuca Vizzarro ) 53761d5bc9bSLuca Vizzarro #: 53861d5bc9bSLuca Vizzarro switch_rx_domain: int | None = field( 53961d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Switch Rx domain: (\d+)") 54061d5bc9bSLuca Vizzarro ) 54161d5bc9bSLuca Vizzarro 54261d5bc9bSLuca Vizzarro 54353eacf3dSLuca Vizzarro@dataclass 54453eacf3dSLuca Vizzarroclass TestPmdPortStats(TextParser): 54553eacf3dSLuca Vizzarro """Port statistics.""" 54653eacf3dSLuca Vizzarro 54753eacf3dSLuca Vizzarro #: 54853eacf3dSLuca Vizzarro port_id: int = field(metadata=TextParser.find_int(r"NIC statistics for port (\d+)")) 54953eacf3dSLuca Vizzarro 55053eacf3dSLuca Vizzarro #: 55153eacf3dSLuca Vizzarro rx_packets: int = field(metadata=TextParser.find_int(r"RX-packets:\s+(\d+)")) 55253eacf3dSLuca Vizzarro #: 55353eacf3dSLuca Vizzarro rx_missed: int = field(metadata=TextParser.find_int(r"RX-missed:\s+(\d+)")) 55453eacf3dSLuca Vizzarro #: 55553eacf3dSLuca Vizzarro rx_bytes: int = field(metadata=TextParser.find_int(r"RX-bytes:\s+(\d+)")) 55653eacf3dSLuca Vizzarro #: 55753eacf3dSLuca Vizzarro rx_errors: int = field(metadata=TextParser.find_int(r"RX-errors:\s+(\d+)")) 55853eacf3dSLuca Vizzarro #: 55953eacf3dSLuca Vizzarro rx_nombuf: int = field(metadata=TextParser.find_int(r"RX-nombuf:\s+(\d+)")) 56053eacf3dSLuca Vizzarro 56153eacf3dSLuca Vizzarro #: 56253eacf3dSLuca Vizzarro tx_packets: int = field(metadata=TextParser.find_int(r"TX-packets:\s+(\d+)")) 56353eacf3dSLuca Vizzarro #: 56453eacf3dSLuca Vizzarro tx_errors: int = field(metadata=TextParser.find_int(r"TX-errors:\s+(\d+)")) 56553eacf3dSLuca Vizzarro #: 56653eacf3dSLuca Vizzarro tx_bytes: int = field(metadata=TextParser.find_int(r"TX-bytes:\s+(\d+)")) 56753eacf3dSLuca Vizzarro 56853eacf3dSLuca Vizzarro #: 56953eacf3dSLuca Vizzarro rx_pps: int = field(metadata=TextParser.find_int(r"Rx-pps:\s+(\d+)")) 57053eacf3dSLuca Vizzarro #: 57153eacf3dSLuca Vizzarro rx_bps: int = field(metadata=TextParser.find_int(r"Rx-bps:\s+(\d+)")) 57253eacf3dSLuca Vizzarro 57353eacf3dSLuca Vizzarro #: 57453eacf3dSLuca Vizzarro tx_pps: int = field(metadata=TextParser.find_int(r"Tx-pps:\s+(\d+)")) 57553eacf3dSLuca Vizzarro #: 57653eacf3dSLuca Vizzarro tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)")) 57753eacf3dSLuca Vizzarro 57853eacf3dSLuca Vizzarro 579*bfad0948SLuca Vizzarroclass TestPmdShell(DPDKShell): 5806ef07151SJuraj Linkeš """Testpmd interactive shell. 5816ef07151SJuraj Linkeš 5826ef07151SJuraj Linkeš The testpmd shell users should never use 5836ef07151SJuraj Linkeš the :meth:`~.interactive_shell.InteractiveShell.send_command` method directly, but rather 5846ef07151SJuraj Linkeš call specialized methods. If there isn't one that satisfies a need, it should be added. 5856ef07151SJuraj Linkeš """ 5866ef07151SJuraj Linkeš 587*bfad0948SLuca Vizzarro _app_params: TestPmdParams 588369d34b8SJeremy Spewock 5896ef07151SJuraj Linkeš #: The path to the testpmd executable. 5906ef07151SJuraj Linkeš path: ClassVar[PurePath] = PurePath("app", "dpdk-testpmd") 5916ef07151SJuraj Linkeš 5926ef07151SJuraj Linkeš #: The testpmd's prompt. 5936ef07151SJuraj Linkeš _default_prompt: ClassVar[str] = "testpmd>" 5946ef07151SJuraj Linkeš 5956ef07151SJuraj Linkeš #: This forces the prompt to appear after sending a command. 5966ef07151SJuraj Linkeš _command_extra_chars: ClassVar[str] = "\n" 597840b1e01SJuraj Linkeš 598*bfad0948SLuca Vizzarro def __init__( 599*bfad0948SLuca Vizzarro self, 600*bfad0948SLuca Vizzarro node: SutNode, 601*bfad0948SLuca Vizzarro privileged: bool = True, 602*bfad0948SLuca Vizzarro timeout: float = SETTINGS.timeout, 603*bfad0948SLuca Vizzarro lcore_filter_specifier: LogicalCoreCount | LogicalCoreList = LogicalCoreCount(), 604*bfad0948SLuca Vizzarro ascending_cores: bool = True, 605*bfad0948SLuca Vizzarro append_prefix_timestamp: bool = True, 606*bfad0948SLuca Vizzarro start_on_init: bool = True, 607*bfad0948SLuca Vizzarro **app_params, 608*bfad0948SLuca Vizzarro ) -> None: 609*bfad0948SLuca Vizzarro """Overrides :meth:`~.dpdk_shell.DPDKShell.__init__`. Changes app_params to kwargs.""" 610*bfad0948SLuca Vizzarro super().__init__( 611*bfad0948SLuca Vizzarro node, 612*bfad0948SLuca Vizzarro privileged, 613*bfad0948SLuca Vizzarro timeout, 614*bfad0948SLuca Vizzarro lcore_filter_specifier, 615*bfad0948SLuca Vizzarro ascending_cores, 616*bfad0948SLuca Vizzarro append_prefix_timestamp, 617*bfad0948SLuca Vizzarro start_on_init, 618*bfad0948SLuca Vizzarro TestPmdParams(**app_params), 619fd8cd8eeSLuca Vizzarro ) 620fd8cd8eeSLuca Vizzarro 621369d34b8SJeremy Spewock def start(self, verify: bool = True) -> None: 622369d34b8SJeremy Spewock """Start packet forwarding with the current configuration. 623369d34b8SJeremy Spewock 624369d34b8SJeremy Spewock Args: 625369d34b8SJeremy Spewock verify: If :data:`True` , a second start command will be sent in an attempt to verify 626369d34b8SJeremy Spewock packet forwarding started as expected. 627369d34b8SJeremy Spewock 628369d34b8SJeremy Spewock Raises: 629369d34b8SJeremy Spewock InteractiveCommandExecutionError: If `verify` is :data:`True` and forwarding fails to 630369d34b8SJeremy Spewock start or ports fail to come up. 631369d34b8SJeremy Spewock """ 632369d34b8SJeremy Spewock self.send_command("start") 633369d34b8SJeremy Spewock if verify: 634369d34b8SJeremy Spewock # If forwarding was already started, sending "start" again should tell us 635369d34b8SJeremy Spewock start_cmd_output = self.send_command("start") 636369d34b8SJeremy Spewock if "Packet forwarding already started" not in start_cmd_output: 637369d34b8SJeremy Spewock self._logger.debug(f"Failed to start packet forwarding: \n{start_cmd_output}") 638369d34b8SJeremy Spewock raise InteractiveCommandExecutionError("Testpmd failed to start packet forwarding.") 639369d34b8SJeremy Spewock 640*bfad0948SLuca Vizzarro number_of_ports = len(self._app_params.ports or []) 641*bfad0948SLuca Vizzarro for port_id in range(number_of_ports): 642369d34b8SJeremy Spewock if not self.wait_link_status_up(port_id): 643369d34b8SJeremy Spewock raise InteractiveCommandExecutionError( 644369d34b8SJeremy Spewock "Not all ports came up after starting packet forwarding in testpmd." 645369d34b8SJeremy Spewock ) 646369d34b8SJeremy Spewock 647369d34b8SJeremy Spewock def stop(self, verify: bool = True) -> None: 648369d34b8SJeremy Spewock """Stop packet forwarding. 649369d34b8SJeremy Spewock 650369d34b8SJeremy Spewock Args: 651369d34b8SJeremy Spewock verify: If :data:`True` , the output of the stop command is scanned to verify that 652369d34b8SJeremy Spewock forwarding was stopped successfully or not started. If neither is found, it is 653369d34b8SJeremy Spewock considered an error. 654369d34b8SJeremy Spewock 655369d34b8SJeremy Spewock Raises: 656369d34b8SJeremy Spewock InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop 657369d34b8SJeremy Spewock forwarding results in an error. 658369d34b8SJeremy Spewock """ 659369d34b8SJeremy Spewock stop_cmd_output = self.send_command("stop") 660369d34b8SJeremy Spewock if verify: 661369d34b8SJeremy Spewock if ( 662369d34b8SJeremy Spewock "Done." not in stop_cmd_output 663369d34b8SJeremy Spewock and "Packet forwarding not started" not in stop_cmd_output 664369d34b8SJeremy Spewock ): 665369d34b8SJeremy Spewock self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}") 666369d34b8SJeremy Spewock raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.") 667369d34b8SJeremy Spewock 668840b1e01SJuraj Linkeš def get_devices(self) -> list[TestPmdDevice]: 6696ef07151SJuraj Linkeš """Get a list of device names that are known to testpmd. 670840b1e01SJuraj Linkeš 6716ef07151SJuraj Linkeš Uses the device info listed in testpmd and then parses the output. 672840b1e01SJuraj Linkeš 673840b1e01SJuraj Linkeš Returns: 6746ef07151SJuraj Linkeš A list of devices. 675840b1e01SJuraj Linkeš """ 676840b1e01SJuraj Linkeš dev_info: str = self.send_command("show device info all") 677840b1e01SJuraj Linkeš dev_list: list[TestPmdDevice] = [] 678840b1e01SJuraj Linkeš for line in dev_info.split("\n"): 679840b1e01SJuraj Linkeš if "device name:" in line.lower(): 680840b1e01SJuraj Linkeš dev_list.append(TestPmdDevice(line)) 681840b1e01SJuraj Linkeš return dev_list 682369d34b8SJeremy Spewock 683369d34b8SJeremy Spewock def wait_link_status_up(self, port_id: int, timeout=SETTINGS.timeout) -> bool: 684369d34b8SJeremy Spewock """Wait until the link status on the given port is "up". 685369d34b8SJeremy Spewock 686369d34b8SJeremy Spewock Arguments: 687369d34b8SJeremy Spewock port_id: Port to check the link status on. 688369d34b8SJeremy Spewock timeout: Time to wait for the link to come up. The default value for this 689369d34b8SJeremy Spewock argument may be modified using the :option:`--timeout` command-line argument 690369d34b8SJeremy Spewock or the :envvar:`DTS_TIMEOUT` environment variable. 691369d34b8SJeremy Spewock 692369d34b8SJeremy Spewock Returns: 693369d34b8SJeremy Spewock Whether the link came up in time or not. 694369d34b8SJeremy Spewock """ 695369d34b8SJeremy Spewock time_to_stop = time.time() + timeout 696369d34b8SJeremy Spewock port_info: str = "" 697369d34b8SJeremy Spewock while time.time() < time_to_stop: 698369d34b8SJeremy Spewock port_info = self.send_command(f"show port info {port_id}") 699369d34b8SJeremy Spewock if "Link status: up" in port_info: 700369d34b8SJeremy Spewock break 701369d34b8SJeremy Spewock time.sleep(0.5) 702369d34b8SJeremy Spewock else: 703369d34b8SJeremy Spewock self._logger.error(f"The link for port {port_id} did not come up in the given timeout.") 704369d34b8SJeremy Spewock return "Link status: up" in port_info 705369d34b8SJeremy Spewock 706fc0f7dc4SLuca Vizzarro def set_forward_mode(self, mode: SimpleForwardingModes, verify: bool = True): 707369d34b8SJeremy Spewock """Set packet forwarding mode. 708369d34b8SJeremy Spewock 709369d34b8SJeremy Spewock Args: 710369d34b8SJeremy Spewock mode: The forwarding mode to use. 711369d34b8SJeremy Spewock verify: If :data:`True` the output of the command will be scanned in an attempt to 712369d34b8SJeremy Spewock verify that the forwarding mode was set to `mode` properly. 713369d34b8SJeremy Spewock 714369d34b8SJeremy Spewock Raises: 715369d34b8SJeremy Spewock InteractiveCommandExecutionError: If `verify` is :data:`True` and the forwarding mode 716369d34b8SJeremy Spewock fails to update. 717369d34b8SJeremy Spewock """ 718369d34b8SJeremy Spewock set_fwd_output = self.send_command(f"set fwd {mode.value}") 719369d34b8SJeremy Spewock if f"Set {mode.value} packet forwarding mode" not in set_fwd_output: 720369d34b8SJeremy Spewock self._logger.debug(f"Failed to set fwd mode to {mode.value}:\n{set_fwd_output}") 721369d34b8SJeremy Spewock raise InteractiveCommandExecutionError( 722369d34b8SJeremy Spewock f"Test pmd failed to set fwd mode to {mode.value}" 723369d34b8SJeremy Spewock ) 724369d34b8SJeremy Spewock 72561d5bc9bSLuca Vizzarro def show_port_info_all(self) -> list[TestPmdPort]: 72661d5bc9bSLuca Vizzarro """Returns the information of all the ports. 72761d5bc9bSLuca Vizzarro 72861d5bc9bSLuca Vizzarro Returns: 72961d5bc9bSLuca Vizzarro list[TestPmdPort]: A list containing all the ports information as `TestPmdPort`. 73061d5bc9bSLuca Vizzarro """ 73161d5bc9bSLuca Vizzarro output = self.send_command("show port info all") 73261d5bc9bSLuca Vizzarro 73361d5bc9bSLuca Vizzarro # Sample output of the "all" command looks like: 73461d5bc9bSLuca Vizzarro # 73561d5bc9bSLuca Vizzarro # <start> 73661d5bc9bSLuca Vizzarro # 73761d5bc9bSLuca Vizzarro # ********************* Infos for port 0 ********************* 73861d5bc9bSLuca Vizzarro # Key: value 73961d5bc9bSLuca Vizzarro # 74061d5bc9bSLuca Vizzarro # ********************* Infos for port 1 ********************* 74161d5bc9bSLuca Vizzarro # Key: value 74261d5bc9bSLuca Vizzarro # <end> 74361d5bc9bSLuca Vizzarro # 74461d5bc9bSLuca Vizzarro # Takes advantage of the double new line in between ports as end delimiter. But we need to 74561d5bc9bSLuca Vizzarro # artificially add a new line at the end to pick up the last port. Because commands are 74661d5bc9bSLuca Vizzarro # executed on a pseudo-terminal created by paramiko on the remote node, lines end with CRLF. 74761d5bc9bSLuca Vizzarro # Therefore we also need to take the carriage return into account. 74861d5bc9bSLuca Vizzarro iter = re.finditer(r"\*{21}.*?[\r\n]{4}", output + "\r\n", re.S) 74961d5bc9bSLuca Vizzarro return [TestPmdPort.parse(block.group(0)) for block in iter] 75061d5bc9bSLuca Vizzarro 75161d5bc9bSLuca Vizzarro def show_port_info(self, port_id: int) -> TestPmdPort: 75261d5bc9bSLuca Vizzarro """Returns the given port information. 75361d5bc9bSLuca Vizzarro 75461d5bc9bSLuca Vizzarro Args: 75561d5bc9bSLuca Vizzarro port_id: The port ID to gather information for. 75661d5bc9bSLuca Vizzarro 75761d5bc9bSLuca Vizzarro Raises: 75861d5bc9bSLuca Vizzarro InteractiveCommandExecutionError: If `port_id` is invalid. 75961d5bc9bSLuca Vizzarro 76061d5bc9bSLuca Vizzarro Returns: 76161d5bc9bSLuca Vizzarro TestPmdPort: An instance of `TestPmdPort` containing the given port's information. 76261d5bc9bSLuca Vizzarro """ 76361d5bc9bSLuca Vizzarro output = self.send_command(f"show port info {port_id}", skip_first_line=True) 76461d5bc9bSLuca Vizzarro if output.startswith("Invalid port"): 76561d5bc9bSLuca Vizzarro raise InteractiveCommandExecutionError("invalid port given") 76661d5bc9bSLuca Vizzarro 76761d5bc9bSLuca Vizzarro return TestPmdPort.parse(output) 76861d5bc9bSLuca Vizzarro 76953eacf3dSLuca Vizzarro def show_port_stats_all(self) -> list[TestPmdPortStats]: 77053eacf3dSLuca Vizzarro """Returns the statistics of all the ports. 77153eacf3dSLuca Vizzarro 77253eacf3dSLuca Vizzarro Returns: 77353eacf3dSLuca Vizzarro list[TestPmdPortStats]: A list containing all the ports stats as `TestPmdPortStats`. 77453eacf3dSLuca Vizzarro """ 77553eacf3dSLuca Vizzarro output = self.send_command("show port stats all") 77653eacf3dSLuca Vizzarro 77753eacf3dSLuca Vizzarro # Sample output of the "all" command looks like: 77853eacf3dSLuca Vizzarro # 77953eacf3dSLuca Vizzarro # ########### NIC statistics for port 0 ########### 78053eacf3dSLuca Vizzarro # values... 78153eacf3dSLuca Vizzarro # ################################################# 78253eacf3dSLuca Vizzarro # 78353eacf3dSLuca Vizzarro # ########### NIC statistics for port 1 ########### 78453eacf3dSLuca Vizzarro # values... 78553eacf3dSLuca Vizzarro # ################################################# 78653eacf3dSLuca Vizzarro # 78753eacf3dSLuca Vizzarro iter = re.finditer(r"(^ #*.+#*$[^#]+)^ #*\r$", output, re.MULTILINE) 78853eacf3dSLuca Vizzarro return [TestPmdPortStats.parse(block.group(1)) for block in iter] 78953eacf3dSLuca Vizzarro 79053eacf3dSLuca Vizzarro def show_port_stats(self, port_id: int) -> TestPmdPortStats: 79153eacf3dSLuca Vizzarro """Returns the given port statistics. 79253eacf3dSLuca Vizzarro 79353eacf3dSLuca Vizzarro Args: 79453eacf3dSLuca Vizzarro port_id: The port ID to gather information for. 79553eacf3dSLuca Vizzarro 79653eacf3dSLuca Vizzarro Raises: 79753eacf3dSLuca Vizzarro InteractiveCommandExecutionError: If `port_id` is invalid. 79853eacf3dSLuca Vizzarro 79953eacf3dSLuca Vizzarro Returns: 80053eacf3dSLuca Vizzarro TestPmdPortStats: An instance of `TestPmdPortStats` containing the given port's stats. 80153eacf3dSLuca Vizzarro """ 80253eacf3dSLuca Vizzarro output = self.send_command(f"show port stats {port_id}", skip_first_line=True) 80353eacf3dSLuca Vizzarro if output.startswith("Invalid port"): 80453eacf3dSLuca Vizzarro raise InteractiveCommandExecutionError("invalid port given") 80553eacf3dSLuca Vizzarro 80653eacf3dSLuca Vizzarro return TestPmdPortStats.parse(output) 80753eacf3dSLuca Vizzarro 808369d34b8SJeremy Spewock def close(self) -> None: 809369d34b8SJeremy Spewock """Overrides :meth:`~.interactive_shell.close`.""" 810369d34b8SJeremy Spewock self.send_command("quit", "") 811369d34b8SJeremy Spewock return super().close() 812