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š 10bfad0948SLuca 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š 172ecfd126SLuca Vizzarroimport functools 1861d5bc9bSLuca Vizzarroimport re 19369d34b8SJeremy Spewockimport time 2061d5bc9bSLuca Vizzarrofrom dataclasses import dataclass, field 2161d5bc9bSLuca Vizzarrofrom enum import Flag, auto 22840b1e01SJuraj Linkešfrom pathlib import PurePath 232ecfd126SLuca Vizzarrofrom typing import Any, Callable, ClassVar, Concatenate, ParamSpec 24840b1e01SJuraj Linkeš 2587ba4cdcSLuca Vizzarrofrom typing_extensions import Self, Unpack 2661d5bc9bSLuca Vizzarro 272ecfd126SLuca Vizzarrofrom framework.exception import InteractiveCommandExecutionError, InternalError 28fc0f7dc4SLuca Vizzarrofrom framework.params.testpmd import SimpleForwardingModes, TestPmdParams 2987ba4cdcSLuca Vizzarrofrom framework.params.types import TestPmdParamsDict 3061d5bc9bSLuca Vizzarrofrom framework.parser import ParserFn, TextParser 31bfad0948SLuca Vizzarrofrom framework.remote_session.dpdk_shell import DPDKShell 32369d34b8SJeremy Spewockfrom framework.settings import SETTINGS 33bfad0948SLuca Vizzarrofrom framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList 34bfad0948SLuca Vizzarrofrom framework.testbed_model.sut_node import SutNode 35369d34b8SJeremy Spewockfrom framework.utils import StrEnum 36369d34b8SJeremy Spewock 372ecfd126SLuca VizzarroP = ParamSpec("P") 382ecfd126SLuca VizzarroTestPmdShellMethod = Callable[Concatenate["TestPmdShell", P], Any] 392ecfd126SLuca Vizzarro 40840b1e01SJuraj Linkeš 413e967643SJuraj Linkešclass TestPmdDevice: 426ef07151SJuraj Linkeš """The data of a device that testpmd can recognize. 436ef07151SJuraj Linkeš 446ef07151SJuraj Linkeš Attributes: 456ef07151SJuraj Linkeš pci_address: The PCI address of the device. 466ef07151SJuraj Linkeš """ 476ef07151SJuraj Linkeš 48840b1e01SJuraj Linkeš pci_address: str 49840b1e01SJuraj Linkeš 50840b1e01SJuraj Linkeš def __init__(self, pci_address_line: str): 516ef07151SJuraj Linkeš """Initialize the device from the testpmd output line string. 526ef07151SJuraj Linkeš 536ef07151SJuraj Linkeš Args: 546ef07151SJuraj Linkeš pci_address_line: A line of testpmd output that contains a device. 556ef07151SJuraj Linkeš """ 56840b1e01SJuraj Linkeš self.pci_address = pci_address_line.strip().split(": ")[1].strip() 57840b1e01SJuraj Linkeš 58840b1e01SJuraj Linkeš def __str__(self) -> str: 596ef07151SJuraj Linkeš """The PCI address captures what the device is.""" 60840b1e01SJuraj Linkeš return self.pci_address 61840b1e01SJuraj Linkeš 62840b1e01SJuraj Linkeš 6361d5bc9bSLuca Vizzarroclass VLANOffloadFlag(Flag): 6461d5bc9bSLuca Vizzarro """Flag representing the VLAN offload settings of a NIC port.""" 6561d5bc9bSLuca Vizzarro 6661d5bc9bSLuca Vizzarro #: 6761d5bc9bSLuca Vizzarro STRIP = auto() 6861d5bc9bSLuca Vizzarro #: 6961d5bc9bSLuca Vizzarro FILTER = auto() 7061d5bc9bSLuca Vizzarro #: 7161d5bc9bSLuca Vizzarro EXTEND = auto() 7261d5bc9bSLuca Vizzarro #: 7361d5bc9bSLuca Vizzarro QINQ_STRIP = auto() 7461d5bc9bSLuca Vizzarro 7561d5bc9bSLuca Vizzarro @classmethod 7661d5bc9bSLuca Vizzarro def from_str_dict(cls, d): 7761d5bc9bSLuca Vizzarro """Makes an instance from a dict containing the flag member names with an "on" value. 7861d5bc9bSLuca Vizzarro 7961d5bc9bSLuca Vizzarro Args: 8061d5bc9bSLuca Vizzarro d: A dictionary containing the flag members as keys and any string value. 8161d5bc9bSLuca Vizzarro 8261d5bc9bSLuca Vizzarro Returns: 8361d5bc9bSLuca Vizzarro A new instance of the flag. 8461d5bc9bSLuca Vizzarro """ 8561d5bc9bSLuca Vizzarro flag = cls(0) 8661d5bc9bSLuca Vizzarro for name in cls.__members__: 8761d5bc9bSLuca Vizzarro if d.get(name) == "on": 8861d5bc9bSLuca Vizzarro flag |= cls[name] 8961d5bc9bSLuca Vizzarro return flag 9061d5bc9bSLuca Vizzarro 9161d5bc9bSLuca Vizzarro @classmethod 9261d5bc9bSLuca Vizzarro def make_parser(cls) -> ParserFn: 9361d5bc9bSLuca Vizzarro """Makes a parser function. 9461d5bc9bSLuca Vizzarro 9561d5bc9bSLuca Vizzarro Returns: 9661d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 9761d5bc9bSLuca Vizzarro parser function that makes an instance of this flag from text. 9861d5bc9bSLuca Vizzarro """ 9961d5bc9bSLuca Vizzarro return TextParser.wrap( 10061d5bc9bSLuca Vizzarro TextParser.find( 10161d5bc9bSLuca Vizzarro r"VLAN offload:\s+" 10261d5bc9bSLuca Vizzarro r"strip (?P<STRIP>on|off), " 10361d5bc9bSLuca Vizzarro r"filter (?P<FILTER>on|off), " 10461d5bc9bSLuca Vizzarro r"extend (?P<EXTEND>on|off), " 10561d5bc9bSLuca Vizzarro r"qinq strip (?P<QINQ_STRIP>on|off)$", 10661d5bc9bSLuca Vizzarro re.MULTILINE, 10761d5bc9bSLuca Vizzarro named=True, 10861d5bc9bSLuca Vizzarro ), 10961d5bc9bSLuca Vizzarro cls.from_str_dict, 11061d5bc9bSLuca Vizzarro ) 11161d5bc9bSLuca Vizzarro 11261d5bc9bSLuca Vizzarro 11361d5bc9bSLuca Vizzarroclass RSSOffloadTypesFlag(Flag): 11461d5bc9bSLuca Vizzarro """Flag representing the RSS offload flow types supported by the NIC port.""" 11561d5bc9bSLuca Vizzarro 11661d5bc9bSLuca Vizzarro #: 11761d5bc9bSLuca Vizzarro ipv4 = auto() 11861d5bc9bSLuca Vizzarro #: 11961d5bc9bSLuca Vizzarro ipv4_frag = auto() 12061d5bc9bSLuca Vizzarro #: 12161d5bc9bSLuca Vizzarro ipv4_tcp = auto() 12261d5bc9bSLuca Vizzarro #: 12361d5bc9bSLuca Vizzarro ipv4_udp = auto() 12461d5bc9bSLuca Vizzarro #: 12561d5bc9bSLuca Vizzarro ipv4_sctp = auto() 12661d5bc9bSLuca Vizzarro #: 12761d5bc9bSLuca Vizzarro ipv4_other = auto() 12861d5bc9bSLuca Vizzarro #: 12961d5bc9bSLuca Vizzarro ipv6 = auto() 13061d5bc9bSLuca Vizzarro #: 13161d5bc9bSLuca Vizzarro ipv6_frag = auto() 13261d5bc9bSLuca Vizzarro #: 13361d5bc9bSLuca Vizzarro ipv6_tcp = auto() 13461d5bc9bSLuca Vizzarro #: 13561d5bc9bSLuca Vizzarro ipv6_udp = auto() 13661d5bc9bSLuca Vizzarro #: 13761d5bc9bSLuca Vizzarro ipv6_sctp = auto() 13861d5bc9bSLuca Vizzarro #: 13961d5bc9bSLuca Vizzarro ipv6_other = auto() 14061d5bc9bSLuca Vizzarro #: 14161d5bc9bSLuca Vizzarro l2_payload = auto() 14261d5bc9bSLuca Vizzarro #: 14361d5bc9bSLuca Vizzarro ipv6_ex = auto() 14461d5bc9bSLuca Vizzarro #: 14561d5bc9bSLuca Vizzarro ipv6_tcp_ex = auto() 14661d5bc9bSLuca Vizzarro #: 14761d5bc9bSLuca Vizzarro ipv6_udp_ex = auto() 14861d5bc9bSLuca Vizzarro #: 14961d5bc9bSLuca Vizzarro port = auto() 15061d5bc9bSLuca Vizzarro #: 15161d5bc9bSLuca Vizzarro vxlan = auto() 15261d5bc9bSLuca Vizzarro #: 15361d5bc9bSLuca Vizzarro geneve = auto() 15461d5bc9bSLuca Vizzarro #: 15561d5bc9bSLuca Vizzarro nvgre = auto() 15661d5bc9bSLuca Vizzarro #: 15761d5bc9bSLuca Vizzarro user_defined_22 = auto() 15861d5bc9bSLuca Vizzarro #: 15961d5bc9bSLuca Vizzarro gtpu = auto() 16061d5bc9bSLuca Vizzarro #: 16161d5bc9bSLuca Vizzarro eth = auto() 16261d5bc9bSLuca Vizzarro #: 16361d5bc9bSLuca Vizzarro s_vlan = auto() 16461d5bc9bSLuca Vizzarro #: 16561d5bc9bSLuca Vizzarro c_vlan = auto() 16661d5bc9bSLuca Vizzarro #: 16761d5bc9bSLuca Vizzarro esp = auto() 16861d5bc9bSLuca Vizzarro #: 16961d5bc9bSLuca Vizzarro ah = auto() 17061d5bc9bSLuca Vizzarro #: 17161d5bc9bSLuca Vizzarro l2tpv3 = auto() 17261d5bc9bSLuca Vizzarro #: 17361d5bc9bSLuca Vizzarro pfcp = auto() 17461d5bc9bSLuca Vizzarro #: 17561d5bc9bSLuca Vizzarro pppoe = auto() 17661d5bc9bSLuca Vizzarro #: 17761d5bc9bSLuca Vizzarro ecpri = auto() 17861d5bc9bSLuca Vizzarro #: 17961d5bc9bSLuca Vizzarro mpls = auto() 18061d5bc9bSLuca Vizzarro #: 18161d5bc9bSLuca Vizzarro ipv4_chksum = auto() 18261d5bc9bSLuca Vizzarro #: 18361d5bc9bSLuca Vizzarro l4_chksum = auto() 18461d5bc9bSLuca Vizzarro #: 18561d5bc9bSLuca Vizzarro l2tpv2 = auto() 18661d5bc9bSLuca Vizzarro #: 18761d5bc9bSLuca Vizzarro ipv6_flow_label = auto() 18861d5bc9bSLuca Vizzarro #: 18961d5bc9bSLuca Vizzarro user_defined_38 = auto() 19061d5bc9bSLuca Vizzarro #: 19161d5bc9bSLuca Vizzarro user_defined_39 = auto() 19261d5bc9bSLuca Vizzarro #: 19361d5bc9bSLuca Vizzarro user_defined_40 = auto() 19461d5bc9bSLuca Vizzarro #: 19561d5bc9bSLuca Vizzarro user_defined_41 = auto() 19661d5bc9bSLuca Vizzarro #: 19761d5bc9bSLuca Vizzarro user_defined_42 = auto() 19861d5bc9bSLuca Vizzarro #: 19961d5bc9bSLuca Vizzarro user_defined_43 = auto() 20061d5bc9bSLuca Vizzarro #: 20161d5bc9bSLuca Vizzarro user_defined_44 = auto() 20261d5bc9bSLuca Vizzarro #: 20361d5bc9bSLuca Vizzarro user_defined_45 = auto() 20461d5bc9bSLuca Vizzarro #: 20561d5bc9bSLuca Vizzarro user_defined_46 = auto() 20661d5bc9bSLuca Vizzarro #: 20761d5bc9bSLuca Vizzarro user_defined_47 = auto() 20861d5bc9bSLuca Vizzarro #: 20961d5bc9bSLuca Vizzarro user_defined_48 = auto() 21061d5bc9bSLuca Vizzarro #: 21161d5bc9bSLuca Vizzarro user_defined_49 = auto() 21261d5bc9bSLuca Vizzarro #: 21361d5bc9bSLuca Vizzarro user_defined_50 = auto() 21461d5bc9bSLuca Vizzarro #: 21561d5bc9bSLuca Vizzarro user_defined_51 = auto() 21661d5bc9bSLuca Vizzarro #: 21761d5bc9bSLuca Vizzarro l3_pre96 = auto() 21861d5bc9bSLuca Vizzarro #: 21961d5bc9bSLuca Vizzarro l3_pre64 = auto() 22061d5bc9bSLuca Vizzarro #: 22161d5bc9bSLuca Vizzarro l3_pre56 = auto() 22261d5bc9bSLuca Vizzarro #: 22361d5bc9bSLuca Vizzarro l3_pre48 = auto() 22461d5bc9bSLuca Vizzarro #: 22561d5bc9bSLuca Vizzarro l3_pre40 = auto() 22661d5bc9bSLuca Vizzarro #: 22761d5bc9bSLuca Vizzarro l3_pre32 = auto() 22861d5bc9bSLuca Vizzarro #: 22961d5bc9bSLuca Vizzarro l2_dst_only = auto() 23061d5bc9bSLuca Vizzarro #: 23161d5bc9bSLuca Vizzarro l2_src_only = auto() 23261d5bc9bSLuca Vizzarro #: 23361d5bc9bSLuca Vizzarro l4_dst_only = auto() 23461d5bc9bSLuca Vizzarro #: 23561d5bc9bSLuca Vizzarro l4_src_only = auto() 23661d5bc9bSLuca Vizzarro #: 23761d5bc9bSLuca Vizzarro l3_dst_only = auto() 23861d5bc9bSLuca Vizzarro #: 23961d5bc9bSLuca Vizzarro l3_src_only = auto() 24061d5bc9bSLuca Vizzarro 24161d5bc9bSLuca Vizzarro #: 24261d5bc9bSLuca Vizzarro ip = ipv4 | ipv4_frag | ipv4_other | ipv6 | ipv6_frag | ipv6_other | ipv6_ex 24361d5bc9bSLuca Vizzarro #: 24461d5bc9bSLuca Vizzarro udp = ipv4_udp | ipv6_udp | ipv6_udp_ex 24561d5bc9bSLuca Vizzarro #: 24661d5bc9bSLuca Vizzarro tcp = ipv4_tcp | ipv6_tcp | ipv6_tcp_ex 24761d5bc9bSLuca Vizzarro #: 24861d5bc9bSLuca Vizzarro sctp = ipv4_sctp | ipv6_sctp 24961d5bc9bSLuca Vizzarro #: 25061d5bc9bSLuca Vizzarro tunnel = vxlan | geneve | nvgre 25161d5bc9bSLuca Vizzarro #: 25261d5bc9bSLuca Vizzarro vlan = s_vlan | c_vlan 25361d5bc9bSLuca Vizzarro #: 25461d5bc9bSLuca Vizzarro all = ( 25561d5bc9bSLuca Vizzarro eth 25661d5bc9bSLuca Vizzarro | vlan 25761d5bc9bSLuca Vizzarro | ip 25861d5bc9bSLuca Vizzarro | tcp 25961d5bc9bSLuca Vizzarro | udp 26061d5bc9bSLuca Vizzarro | sctp 26161d5bc9bSLuca Vizzarro | l2_payload 26261d5bc9bSLuca Vizzarro | l2tpv3 26361d5bc9bSLuca Vizzarro | esp 26461d5bc9bSLuca Vizzarro | ah 26561d5bc9bSLuca Vizzarro | pfcp 26661d5bc9bSLuca Vizzarro | gtpu 26761d5bc9bSLuca Vizzarro | ecpri 26861d5bc9bSLuca Vizzarro | mpls 26961d5bc9bSLuca Vizzarro | l2tpv2 27061d5bc9bSLuca Vizzarro ) 27161d5bc9bSLuca Vizzarro 27261d5bc9bSLuca Vizzarro @classmethod 27361d5bc9bSLuca Vizzarro def from_list_string(cls, names: str) -> Self: 27461d5bc9bSLuca Vizzarro """Makes a flag from a whitespace-separated list of names. 27561d5bc9bSLuca Vizzarro 27661d5bc9bSLuca Vizzarro Args: 27761d5bc9bSLuca Vizzarro names: a whitespace-separated list containing the members of this flag. 27861d5bc9bSLuca Vizzarro 27961d5bc9bSLuca Vizzarro Returns: 28061d5bc9bSLuca Vizzarro An instance of this flag. 28161d5bc9bSLuca Vizzarro """ 28261d5bc9bSLuca Vizzarro flag = cls(0) 28361d5bc9bSLuca Vizzarro for name in names.split(): 28461d5bc9bSLuca Vizzarro flag |= cls.from_str(name) 28561d5bc9bSLuca Vizzarro return flag 28661d5bc9bSLuca Vizzarro 28761d5bc9bSLuca Vizzarro @classmethod 28861d5bc9bSLuca Vizzarro def from_str(cls, name: str) -> Self: 28961d5bc9bSLuca Vizzarro """Makes a flag matching the supplied name. 29061d5bc9bSLuca Vizzarro 29161d5bc9bSLuca Vizzarro Args: 29261d5bc9bSLuca Vizzarro name: a valid member of this flag in text 29361d5bc9bSLuca Vizzarro Returns: 29461d5bc9bSLuca Vizzarro An instance of this flag. 29561d5bc9bSLuca Vizzarro """ 29661d5bc9bSLuca Vizzarro member_name = name.strip().replace("-", "_") 29761d5bc9bSLuca Vizzarro return cls[member_name] 29861d5bc9bSLuca Vizzarro 29961d5bc9bSLuca Vizzarro @classmethod 30061d5bc9bSLuca Vizzarro def make_parser(cls) -> ParserFn: 30161d5bc9bSLuca Vizzarro """Makes a parser function. 30261d5bc9bSLuca Vizzarro 30361d5bc9bSLuca Vizzarro Returns: 30461d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 30561d5bc9bSLuca Vizzarro parser function that makes an instance of this flag from text. 30661d5bc9bSLuca Vizzarro """ 30761d5bc9bSLuca Vizzarro return TextParser.wrap( 30861d5bc9bSLuca Vizzarro TextParser.find(r"Supported RSS offload flow types:((?:\r?\n? \S+)+)", re.MULTILINE), 30961d5bc9bSLuca Vizzarro RSSOffloadTypesFlag.from_list_string, 31061d5bc9bSLuca Vizzarro ) 31161d5bc9bSLuca Vizzarro 31261d5bc9bSLuca Vizzarro 31361d5bc9bSLuca Vizzarroclass DeviceCapabilitiesFlag(Flag): 31461d5bc9bSLuca Vizzarro """Flag representing the device capabilities.""" 31561d5bc9bSLuca Vizzarro 31661d5bc9bSLuca Vizzarro #: Device supports Rx queue setup after device started. 31761d5bc9bSLuca Vizzarro RUNTIME_RX_QUEUE_SETUP = auto() 31861d5bc9bSLuca Vizzarro #: Device supports Tx queue setup after device started. 31961d5bc9bSLuca Vizzarro RUNTIME_TX_QUEUE_SETUP = auto() 32061d5bc9bSLuca Vizzarro #: Device supports shared Rx queue among ports within Rx domain and switch domain. 32161d5bc9bSLuca Vizzarro RXQ_SHARE = auto() 32261d5bc9bSLuca Vizzarro #: Device supports keeping flow rules across restart. 32361d5bc9bSLuca Vizzarro FLOW_RULE_KEEP = auto() 32461d5bc9bSLuca Vizzarro #: Device supports keeping shared flow objects across restart. 32561d5bc9bSLuca Vizzarro FLOW_SHARED_OBJECT_KEEP = auto() 32661d5bc9bSLuca Vizzarro 32761d5bc9bSLuca Vizzarro @classmethod 32861d5bc9bSLuca Vizzarro def make_parser(cls) -> ParserFn: 32961d5bc9bSLuca Vizzarro """Makes a parser function. 33061d5bc9bSLuca Vizzarro 33161d5bc9bSLuca Vizzarro Returns: 33261d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 33361d5bc9bSLuca Vizzarro parser function that makes an instance of this flag from text. 33461d5bc9bSLuca Vizzarro """ 33561d5bc9bSLuca Vizzarro return TextParser.wrap( 33661d5bc9bSLuca Vizzarro TextParser.find_int(r"Device capabilities: (0x[A-Fa-f\d]+)"), 33761d5bc9bSLuca Vizzarro cls, 33861d5bc9bSLuca Vizzarro ) 33961d5bc9bSLuca Vizzarro 34061d5bc9bSLuca Vizzarro 34161d5bc9bSLuca Vizzarroclass DeviceErrorHandlingMode(StrEnum): 34261d5bc9bSLuca Vizzarro """Enum representing the device error handling mode.""" 34361d5bc9bSLuca Vizzarro 34461d5bc9bSLuca Vizzarro #: 34561d5bc9bSLuca Vizzarro none = auto() 34661d5bc9bSLuca Vizzarro #: 34761d5bc9bSLuca Vizzarro passive = auto() 34861d5bc9bSLuca Vizzarro #: 34961d5bc9bSLuca Vizzarro proactive = auto() 35061d5bc9bSLuca Vizzarro #: 35161d5bc9bSLuca Vizzarro unknown = auto() 35261d5bc9bSLuca Vizzarro 35361d5bc9bSLuca Vizzarro @classmethod 35461d5bc9bSLuca Vizzarro def make_parser(cls) -> ParserFn: 35561d5bc9bSLuca Vizzarro """Makes a parser function. 35661d5bc9bSLuca Vizzarro 35761d5bc9bSLuca Vizzarro Returns: 35861d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a 35961d5bc9bSLuca Vizzarro parser function that makes an instance of this enum from text. 36061d5bc9bSLuca Vizzarro """ 36161d5bc9bSLuca Vizzarro return TextParser.wrap(TextParser.find(r"Device error handling mode: (\w+)"), cls) 36261d5bc9bSLuca Vizzarro 36361d5bc9bSLuca Vizzarro 36461d5bc9bSLuca Vizzarrodef make_device_private_info_parser() -> ParserFn: 36561d5bc9bSLuca Vizzarro """Device private information parser. 36661d5bc9bSLuca Vizzarro 36761d5bc9bSLuca Vizzarro Ensures that we are not parsing invalid device private info output. 36861d5bc9bSLuca Vizzarro 36961d5bc9bSLuca Vizzarro Returns: 37061d5bc9bSLuca Vizzarro ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a parser 37161d5bc9bSLuca Vizzarro function that parses the device private info from the TestPmd port info output. 37261d5bc9bSLuca Vizzarro """ 37361d5bc9bSLuca Vizzarro 37461d5bc9bSLuca Vizzarro def _validate(info: str): 37561d5bc9bSLuca Vizzarro info = info.strip() 37661d5bc9bSLuca Vizzarro if info == "none" or info.startswith("Invalid file") or info.startswith("Failed to dump"): 37761d5bc9bSLuca Vizzarro return None 37861d5bc9bSLuca Vizzarro return info 37961d5bc9bSLuca Vizzarro 38061d5bc9bSLuca Vizzarro return TextParser.wrap(TextParser.find(r"Device private info:\s+([\s\S]+)"), _validate) 38161d5bc9bSLuca Vizzarro 38261d5bc9bSLuca Vizzarro 38361d5bc9bSLuca Vizzarro@dataclass 38461d5bc9bSLuca Vizzarroclass TestPmdPort(TextParser): 38561d5bc9bSLuca Vizzarro """Dataclass representing the result of testpmd's ``show port info`` command.""" 38661d5bc9bSLuca Vizzarro 38761d5bc9bSLuca Vizzarro #: 38861d5bc9bSLuca Vizzarro id: int = field(metadata=TextParser.find_int(r"Infos for port (\d+)\b")) 38961d5bc9bSLuca Vizzarro #: 39061d5bc9bSLuca Vizzarro device_name: str = field(metadata=TextParser.find(r"Device name: ([^\r\n]+)")) 39161d5bc9bSLuca Vizzarro #: 39261d5bc9bSLuca Vizzarro driver_name: str = field(metadata=TextParser.find(r"Driver name: ([^\r\n]+)")) 39361d5bc9bSLuca Vizzarro #: 39461d5bc9bSLuca Vizzarro socket_id: int = field(metadata=TextParser.find_int(r"Connect to socket: (\d+)")) 39561d5bc9bSLuca Vizzarro #: 39661d5bc9bSLuca Vizzarro is_link_up: bool = field(metadata=TextParser.find("Link status: up")) 39761d5bc9bSLuca Vizzarro #: 39861d5bc9bSLuca Vizzarro link_speed: str = field(metadata=TextParser.find(r"Link speed: ([^\r\n]+)")) 39961d5bc9bSLuca Vizzarro #: 40061d5bc9bSLuca Vizzarro is_link_full_duplex: bool = field(metadata=TextParser.find("Link duplex: full-duplex")) 40161d5bc9bSLuca Vizzarro #: 40261d5bc9bSLuca Vizzarro is_link_autonegotiated: bool = field(metadata=TextParser.find("Autoneg status: On")) 40361d5bc9bSLuca Vizzarro #: 40461d5bc9bSLuca Vizzarro is_promiscuous_mode_enabled: bool = field(metadata=TextParser.find("Promiscuous mode: enabled")) 40561d5bc9bSLuca Vizzarro #: 40661d5bc9bSLuca Vizzarro is_allmulticast_mode_enabled: bool = field( 40761d5bc9bSLuca Vizzarro metadata=TextParser.find("Allmulticast mode: enabled") 40861d5bc9bSLuca Vizzarro ) 40961d5bc9bSLuca Vizzarro #: Maximum number of MAC addresses 41061d5bc9bSLuca Vizzarro max_mac_addresses_num: int = field( 41161d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Maximum number of MAC addresses: (\d+)") 41261d5bc9bSLuca Vizzarro ) 41361d5bc9bSLuca Vizzarro #: Maximum configurable length of RX packet 41461d5bc9bSLuca Vizzarro max_hash_mac_addresses_num: int = field( 41561d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Maximum number of MAC addresses of hash filtering: (\d+)") 41661d5bc9bSLuca Vizzarro ) 41761d5bc9bSLuca Vizzarro #: Minimum size of RX buffer 41861d5bc9bSLuca Vizzarro min_rx_bufsize: int = field(metadata=TextParser.find_int(r"Minimum size of RX buffer: (\d+)")) 41961d5bc9bSLuca Vizzarro #: Maximum configurable length of RX packet 42061d5bc9bSLuca Vizzarro max_rx_packet_length: int = field( 42161d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Maximum configurable length of RX packet: (\d+)") 42261d5bc9bSLuca Vizzarro ) 42361d5bc9bSLuca Vizzarro #: Maximum configurable size of LRO aggregated packet 42461d5bc9bSLuca Vizzarro max_lro_packet_size: int = field( 42561d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Maximum configurable size of LRO aggregated packet: (\d+)") 42661d5bc9bSLuca Vizzarro ) 42761d5bc9bSLuca Vizzarro 42861d5bc9bSLuca Vizzarro #: Current number of RX queues 42961d5bc9bSLuca Vizzarro rx_queues_num: int = field(metadata=TextParser.find_int(r"Current number of RX queues: (\d+)")) 43061d5bc9bSLuca Vizzarro #: Max possible RX queues 43161d5bc9bSLuca Vizzarro max_rx_queues_num: int = field(metadata=TextParser.find_int(r"Max possible RX queues: (\d+)")) 43261d5bc9bSLuca Vizzarro #: Max possible number of RXDs per queue 43361d5bc9bSLuca Vizzarro max_queue_rxd_num: int = field( 43461d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Max possible number of RXDs per queue: (\d+)") 43561d5bc9bSLuca Vizzarro ) 43661d5bc9bSLuca Vizzarro #: Min possible number of RXDs per queue 43761d5bc9bSLuca Vizzarro min_queue_rxd_num: int = field( 43861d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Min possible number of RXDs per queue: (\d+)") 43961d5bc9bSLuca Vizzarro ) 44061d5bc9bSLuca Vizzarro #: RXDs number alignment 44161d5bc9bSLuca Vizzarro rxd_alignment_num: int = field(metadata=TextParser.find_int(r"RXDs number alignment: (\d+)")) 44261d5bc9bSLuca Vizzarro 44361d5bc9bSLuca Vizzarro #: Current number of TX queues 44461d5bc9bSLuca Vizzarro tx_queues_num: int = field(metadata=TextParser.find_int(r"Current number of TX queues: (\d+)")) 44561d5bc9bSLuca Vizzarro #: Max possible TX queues 44661d5bc9bSLuca Vizzarro max_tx_queues_num: int = field(metadata=TextParser.find_int(r"Max possible TX queues: (\d+)")) 44761d5bc9bSLuca Vizzarro #: Max possible number of TXDs per queue 44861d5bc9bSLuca Vizzarro max_queue_txd_num: int = field( 44961d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Max possible number of TXDs per queue: (\d+)") 45061d5bc9bSLuca Vizzarro ) 45161d5bc9bSLuca Vizzarro #: Min possible number of TXDs per queue 45261d5bc9bSLuca Vizzarro min_queue_txd_num: int = field( 45361d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Min possible number of TXDs per queue: (\d+)") 45461d5bc9bSLuca Vizzarro ) 45561d5bc9bSLuca Vizzarro #: TXDs number alignment 45661d5bc9bSLuca Vizzarro txd_alignment_num: int = field(metadata=TextParser.find_int(r"TXDs number alignment: (\d+)")) 45761d5bc9bSLuca Vizzarro #: Max segment number per packet 45861d5bc9bSLuca Vizzarro max_packet_segment_num: int = field( 45961d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Max segment number per packet: (\d+)") 46061d5bc9bSLuca Vizzarro ) 46161d5bc9bSLuca Vizzarro #: Max segment number per MTU/TSO 46261d5bc9bSLuca Vizzarro max_mtu_segment_num: int = field( 46361d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"Max segment number per MTU\/TSO: (\d+)") 46461d5bc9bSLuca Vizzarro ) 46561d5bc9bSLuca Vizzarro 46661d5bc9bSLuca Vizzarro #: 46761d5bc9bSLuca Vizzarro device_capabilities: DeviceCapabilitiesFlag = field( 46861d5bc9bSLuca Vizzarro metadata=DeviceCapabilitiesFlag.make_parser(), 46961d5bc9bSLuca Vizzarro ) 47061d5bc9bSLuca Vizzarro #: 471*618d9140SJuraj Linkeš device_error_handling_mode: DeviceErrorHandlingMode | None = field( 472*618d9140SJuraj Linkeš default=None, metadata=DeviceErrorHandlingMode.make_parser() 47361d5bc9bSLuca Vizzarro ) 47461d5bc9bSLuca Vizzarro #: 47561d5bc9bSLuca Vizzarro device_private_info: str | None = field( 47661d5bc9bSLuca Vizzarro default=None, 47761d5bc9bSLuca Vizzarro metadata=make_device_private_info_parser(), 47861d5bc9bSLuca Vizzarro ) 47961d5bc9bSLuca Vizzarro 48061d5bc9bSLuca Vizzarro #: 48161d5bc9bSLuca Vizzarro hash_key_size: int | None = field( 48261d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Hash key size in bytes: (\d+)") 48361d5bc9bSLuca Vizzarro ) 48461d5bc9bSLuca Vizzarro #: 48561d5bc9bSLuca Vizzarro redirection_table_size: int | None = field( 48661d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Redirection table size: (\d+)") 48761d5bc9bSLuca Vizzarro ) 48861d5bc9bSLuca Vizzarro #: 48961d5bc9bSLuca Vizzarro supported_rss_offload_flow_types: RSSOffloadTypesFlag = field( 49061d5bc9bSLuca Vizzarro default=RSSOffloadTypesFlag(0), metadata=RSSOffloadTypesFlag.make_parser() 49161d5bc9bSLuca Vizzarro ) 49261d5bc9bSLuca Vizzarro 49361d5bc9bSLuca Vizzarro #: 49461d5bc9bSLuca Vizzarro mac_address: str | None = field( 49561d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find(r"MAC address: ([A-Fa-f0-9:]+)") 49661d5bc9bSLuca Vizzarro ) 49761d5bc9bSLuca Vizzarro #: 49861d5bc9bSLuca Vizzarro fw_version: str | None = field( 49961d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find(r"Firmware-version: ([^\r\n]+)") 50061d5bc9bSLuca Vizzarro ) 50161d5bc9bSLuca Vizzarro #: 50261d5bc9bSLuca Vizzarro dev_args: str | None = field(default=None, metadata=TextParser.find(r"Devargs: ([^\r\n]+)")) 50361d5bc9bSLuca Vizzarro #: Socket id of the memory allocation 50461d5bc9bSLuca Vizzarro mem_alloc_socket_id: int | None = field( 50561d5bc9bSLuca Vizzarro default=None, 50661d5bc9bSLuca Vizzarro metadata=TextParser.find_int(r"memory allocation on the socket: (\d+)"), 50761d5bc9bSLuca Vizzarro ) 50861d5bc9bSLuca Vizzarro #: 50961d5bc9bSLuca Vizzarro mtu: int | None = field(default=None, metadata=TextParser.find_int(r"MTU: (\d+)")) 51061d5bc9bSLuca Vizzarro 51161d5bc9bSLuca Vizzarro #: 51261d5bc9bSLuca Vizzarro vlan_offload: VLANOffloadFlag | None = field( 51361d5bc9bSLuca Vizzarro default=None, 51461d5bc9bSLuca Vizzarro metadata=VLANOffloadFlag.make_parser(), 51561d5bc9bSLuca Vizzarro ) 51661d5bc9bSLuca Vizzarro 51761d5bc9bSLuca Vizzarro #: Maximum size of RX buffer 51861d5bc9bSLuca Vizzarro max_rx_bufsize: int | None = field( 51961d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Maximum size of RX buffer: (\d+)") 52061d5bc9bSLuca Vizzarro ) 52161d5bc9bSLuca Vizzarro #: Maximum number of VFs 52261d5bc9bSLuca Vizzarro max_vfs_num: int | None = field( 52361d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Maximum number of VFs: (\d+)") 52461d5bc9bSLuca Vizzarro ) 52561d5bc9bSLuca Vizzarro #: Maximum number of VMDq pools 52661d5bc9bSLuca Vizzarro max_vmdq_pools_num: int | None = field( 52761d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Maximum number of VMDq pools: (\d+)") 52861d5bc9bSLuca Vizzarro ) 52961d5bc9bSLuca Vizzarro 53061d5bc9bSLuca Vizzarro #: 53161d5bc9bSLuca Vizzarro switch_name: str | None = field( 53261d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find(r"Switch name: ([\r\n]+)") 53361d5bc9bSLuca Vizzarro ) 53461d5bc9bSLuca Vizzarro #: 53561d5bc9bSLuca Vizzarro switch_domain_id: int | None = field( 53661d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Switch domain Id: (\d+)") 53761d5bc9bSLuca Vizzarro ) 53861d5bc9bSLuca Vizzarro #: 53961d5bc9bSLuca Vizzarro switch_port_id: int | None = field( 54061d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Switch Port Id: (\d+)") 54161d5bc9bSLuca Vizzarro ) 54261d5bc9bSLuca Vizzarro #: 54361d5bc9bSLuca Vizzarro switch_rx_domain: int | None = field( 54461d5bc9bSLuca Vizzarro default=None, metadata=TextParser.find_int(r"Switch Rx domain: (\d+)") 54561d5bc9bSLuca Vizzarro ) 54661d5bc9bSLuca Vizzarro 54761d5bc9bSLuca Vizzarro 54853eacf3dSLuca Vizzarro@dataclass 54953eacf3dSLuca Vizzarroclass TestPmdPortStats(TextParser): 55053eacf3dSLuca Vizzarro """Port statistics.""" 55153eacf3dSLuca Vizzarro 55253eacf3dSLuca Vizzarro #: 55353eacf3dSLuca Vizzarro port_id: int = field(metadata=TextParser.find_int(r"NIC statistics for port (\d+)")) 55453eacf3dSLuca Vizzarro 55553eacf3dSLuca Vizzarro #: 55653eacf3dSLuca Vizzarro rx_packets: int = field(metadata=TextParser.find_int(r"RX-packets:\s+(\d+)")) 55753eacf3dSLuca Vizzarro #: 55853eacf3dSLuca Vizzarro rx_missed: int = field(metadata=TextParser.find_int(r"RX-missed:\s+(\d+)")) 55953eacf3dSLuca Vizzarro #: 56053eacf3dSLuca Vizzarro rx_bytes: int = field(metadata=TextParser.find_int(r"RX-bytes:\s+(\d+)")) 56153eacf3dSLuca Vizzarro #: 56253eacf3dSLuca Vizzarro rx_errors: int = field(metadata=TextParser.find_int(r"RX-errors:\s+(\d+)")) 56353eacf3dSLuca Vizzarro #: 56453eacf3dSLuca Vizzarro rx_nombuf: int = field(metadata=TextParser.find_int(r"RX-nombuf:\s+(\d+)")) 56553eacf3dSLuca Vizzarro 56653eacf3dSLuca Vizzarro #: 56753eacf3dSLuca Vizzarro tx_packets: int = field(metadata=TextParser.find_int(r"TX-packets:\s+(\d+)")) 56853eacf3dSLuca Vizzarro #: 56953eacf3dSLuca Vizzarro tx_errors: int = field(metadata=TextParser.find_int(r"TX-errors:\s+(\d+)")) 57053eacf3dSLuca Vizzarro #: 57153eacf3dSLuca Vizzarro tx_bytes: int = field(metadata=TextParser.find_int(r"TX-bytes:\s+(\d+)")) 57253eacf3dSLuca Vizzarro 57353eacf3dSLuca Vizzarro #: 57453eacf3dSLuca Vizzarro rx_pps: int = field(metadata=TextParser.find_int(r"Rx-pps:\s+(\d+)")) 57553eacf3dSLuca Vizzarro #: 57653eacf3dSLuca Vizzarro rx_bps: int = field(metadata=TextParser.find_int(r"Rx-bps:\s+(\d+)")) 57753eacf3dSLuca Vizzarro 57853eacf3dSLuca Vizzarro #: 57953eacf3dSLuca Vizzarro tx_pps: int = field(metadata=TextParser.find_int(r"Tx-pps:\s+(\d+)")) 58053eacf3dSLuca Vizzarro #: 58153eacf3dSLuca Vizzarro tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)")) 58253eacf3dSLuca Vizzarro 58353eacf3dSLuca Vizzarro 5842ecfd126SLuca Vizzarrodef requires_stopped_ports(func: TestPmdShellMethod) -> TestPmdShellMethod: 5852ecfd126SLuca Vizzarro """Decorator for :class:`TestPmdShell` commands methods that require stopped ports. 5862ecfd126SLuca Vizzarro 5872ecfd126SLuca Vizzarro If the decorated method is called while the ports are started, then these are stopped before 5882ecfd126SLuca Vizzarro continuing. 5892ecfd126SLuca Vizzarro 5902ecfd126SLuca Vizzarro Args: 5912ecfd126SLuca Vizzarro func: The :class:`TestPmdShell` method to decorate. 5922ecfd126SLuca Vizzarro """ 5932ecfd126SLuca Vizzarro 5942ecfd126SLuca Vizzarro @functools.wraps(func) 5952ecfd126SLuca Vizzarro def _wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs): 5962ecfd126SLuca Vizzarro if self.ports_started: 5972ecfd126SLuca Vizzarro self._logger.debug("Ports need to be stopped to continue.") 5982ecfd126SLuca Vizzarro self.stop_all_ports() 5992ecfd126SLuca Vizzarro 6002ecfd126SLuca Vizzarro return func(self, *args, **kwargs) 6012ecfd126SLuca Vizzarro 6022ecfd126SLuca Vizzarro return _wrapper 6032ecfd126SLuca Vizzarro 6042ecfd126SLuca Vizzarro 6052ecfd126SLuca Vizzarrodef requires_started_ports(func: TestPmdShellMethod) -> TestPmdShellMethod: 6062ecfd126SLuca Vizzarro """Decorator for :class:`TestPmdShell` commands methods that require started ports. 6072ecfd126SLuca Vizzarro 6082ecfd126SLuca Vizzarro If the decorated method is called while the ports are stopped, then these are started before 6092ecfd126SLuca Vizzarro continuing. 6102ecfd126SLuca Vizzarro 6112ecfd126SLuca Vizzarro Args: 6122ecfd126SLuca Vizzarro func: The :class:`TestPmdShell` method to decorate. 6132ecfd126SLuca Vizzarro """ 6142ecfd126SLuca Vizzarro 6152ecfd126SLuca Vizzarro @functools.wraps(func) 6162ecfd126SLuca Vizzarro def _wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs): 6172ecfd126SLuca Vizzarro if not self.ports_started: 6182ecfd126SLuca Vizzarro self._logger.debug("Ports need to be started to continue.") 6192ecfd126SLuca Vizzarro self.start_all_ports() 6202ecfd126SLuca Vizzarro 6212ecfd126SLuca Vizzarro return func(self, *args, **kwargs) 6222ecfd126SLuca Vizzarro 6232ecfd126SLuca Vizzarro return _wrapper 6242ecfd126SLuca Vizzarro 6252ecfd126SLuca Vizzarro 626bfad0948SLuca Vizzarroclass TestPmdShell(DPDKShell): 6276ef07151SJuraj Linkeš """Testpmd interactive shell. 6286ef07151SJuraj Linkeš 6296ef07151SJuraj Linkeš The testpmd shell users should never use 6306ef07151SJuraj Linkeš the :meth:`~.interactive_shell.InteractiveShell.send_command` method directly, but rather 6316ef07151SJuraj Linkeš call specialized methods. If there isn't one that satisfies a need, it should be added. 6322ecfd126SLuca Vizzarro 6332ecfd126SLuca Vizzarro Attributes: 6342ecfd126SLuca Vizzarro ports_started: Indicates whether the ports are started. 6356ef07151SJuraj Linkeš """ 6366ef07151SJuraj Linkeš 637bfad0948SLuca Vizzarro _app_params: TestPmdParams 638369d34b8SJeremy Spewock 6396ef07151SJuraj Linkeš #: The path to the testpmd executable. 6406ef07151SJuraj Linkeš path: ClassVar[PurePath] = PurePath("app", "dpdk-testpmd") 6416ef07151SJuraj Linkeš 6426ef07151SJuraj Linkeš #: The testpmd's prompt. 6436ef07151SJuraj Linkeš _default_prompt: ClassVar[str] = "testpmd>" 6446ef07151SJuraj Linkeš 6456ef07151SJuraj Linkeš #: This forces the prompt to appear after sending a command. 6466ef07151SJuraj Linkeš _command_extra_chars: ClassVar[str] = "\n" 647840b1e01SJuraj Linkeš 6482ecfd126SLuca Vizzarro ports_started: bool 6492ecfd126SLuca Vizzarro 650bfad0948SLuca Vizzarro def __init__( 651bfad0948SLuca Vizzarro self, 652bfad0948SLuca Vizzarro node: SutNode, 653bfad0948SLuca Vizzarro privileged: bool = True, 654bfad0948SLuca Vizzarro timeout: float = SETTINGS.timeout, 655bfad0948SLuca Vizzarro lcore_filter_specifier: LogicalCoreCount | LogicalCoreList = LogicalCoreCount(), 656bfad0948SLuca Vizzarro ascending_cores: bool = True, 657bfad0948SLuca Vizzarro append_prefix_timestamp: bool = True, 65865a1b4e8SJeremy Spewock name: str | None = None, 65987ba4cdcSLuca Vizzarro **app_params: Unpack[TestPmdParamsDict], 660bfad0948SLuca Vizzarro ) -> None: 661bfad0948SLuca Vizzarro """Overrides :meth:`~.dpdk_shell.DPDKShell.__init__`. Changes app_params to kwargs.""" 662bfad0948SLuca Vizzarro super().__init__( 663bfad0948SLuca Vizzarro node, 664bfad0948SLuca Vizzarro privileged, 665bfad0948SLuca Vizzarro timeout, 666bfad0948SLuca Vizzarro lcore_filter_specifier, 667bfad0948SLuca Vizzarro ascending_cores, 668bfad0948SLuca Vizzarro append_prefix_timestamp, 669bfad0948SLuca Vizzarro TestPmdParams(**app_params), 67065a1b4e8SJeremy Spewock name, 671fd8cd8eeSLuca Vizzarro ) 672fd8cd8eeSLuca Vizzarro 6732ecfd126SLuca Vizzarro self.ports_started = not self._app_params.disable_device_start 6742ecfd126SLuca Vizzarro 6752ecfd126SLuca Vizzarro @requires_started_ports 676369d34b8SJeremy Spewock def start(self, verify: bool = True) -> None: 677369d34b8SJeremy Spewock """Start packet forwarding with the current configuration. 678369d34b8SJeremy Spewock 679369d34b8SJeremy Spewock Args: 680369d34b8SJeremy Spewock verify: If :data:`True` , a second start command will be sent in an attempt to verify 681369d34b8SJeremy Spewock packet forwarding started as expected. 682369d34b8SJeremy Spewock 683369d34b8SJeremy Spewock Raises: 684369d34b8SJeremy Spewock InteractiveCommandExecutionError: If `verify` is :data:`True` and forwarding fails to 685369d34b8SJeremy Spewock start or ports fail to come up. 686369d34b8SJeremy Spewock """ 687369d34b8SJeremy Spewock self.send_command("start") 688369d34b8SJeremy Spewock if verify: 689369d34b8SJeremy Spewock # If forwarding was already started, sending "start" again should tell us 690369d34b8SJeremy Spewock start_cmd_output = self.send_command("start") 691369d34b8SJeremy Spewock if "Packet forwarding already started" not in start_cmd_output: 692369d34b8SJeremy Spewock self._logger.debug(f"Failed to start packet forwarding: \n{start_cmd_output}") 693369d34b8SJeremy Spewock raise InteractiveCommandExecutionError("Testpmd failed to start packet forwarding.") 694369d34b8SJeremy Spewock 695bfad0948SLuca Vizzarro number_of_ports = len(self._app_params.ports or []) 696bfad0948SLuca Vizzarro for port_id in range(number_of_ports): 697369d34b8SJeremy Spewock if not self.wait_link_status_up(port_id): 698369d34b8SJeremy Spewock raise InteractiveCommandExecutionError( 699369d34b8SJeremy Spewock "Not all ports came up after starting packet forwarding in testpmd." 700369d34b8SJeremy Spewock ) 701369d34b8SJeremy Spewock 702369d34b8SJeremy Spewock def stop(self, verify: bool = True) -> None: 703369d34b8SJeremy Spewock """Stop packet forwarding. 704369d34b8SJeremy Spewock 705369d34b8SJeremy Spewock Args: 706369d34b8SJeremy Spewock verify: If :data:`True` , the output of the stop command is scanned to verify that 707369d34b8SJeremy Spewock forwarding was stopped successfully or not started. If neither is found, it is 708369d34b8SJeremy Spewock considered an error. 709369d34b8SJeremy Spewock 710369d34b8SJeremy Spewock Raises: 711369d34b8SJeremy Spewock InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop 712369d34b8SJeremy Spewock forwarding results in an error. 713369d34b8SJeremy Spewock """ 714369d34b8SJeremy Spewock stop_cmd_output = self.send_command("stop") 715369d34b8SJeremy Spewock if verify: 716369d34b8SJeremy Spewock if ( 717369d34b8SJeremy Spewock "Done." not in stop_cmd_output 718369d34b8SJeremy Spewock and "Packet forwarding not started" not in stop_cmd_output 719369d34b8SJeremy Spewock ): 720369d34b8SJeremy Spewock self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}") 721369d34b8SJeremy Spewock raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.") 722369d34b8SJeremy Spewock 723840b1e01SJuraj Linkeš def get_devices(self) -> list[TestPmdDevice]: 7246ef07151SJuraj Linkeš """Get a list of device names that are known to testpmd. 725840b1e01SJuraj Linkeš 7266ef07151SJuraj Linkeš Uses the device info listed in testpmd and then parses the output. 727840b1e01SJuraj Linkeš 728840b1e01SJuraj Linkeš Returns: 7296ef07151SJuraj Linkeš A list of devices. 730840b1e01SJuraj Linkeš """ 731840b1e01SJuraj Linkeš dev_info: str = self.send_command("show device info all") 732840b1e01SJuraj Linkeš dev_list: list[TestPmdDevice] = [] 733840b1e01SJuraj Linkeš for line in dev_info.split("\n"): 734840b1e01SJuraj Linkeš if "device name:" in line.lower(): 735840b1e01SJuraj Linkeš dev_list.append(TestPmdDevice(line)) 736840b1e01SJuraj Linkeš return dev_list 737369d34b8SJeremy Spewock 738369d34b8SJeremy Spewock def wait_link_status_up(self, port_id: int, timeout=SETTINGS.timeout) -> bool: 739369d34b8SJeremy Spewock """Wait until the link status on the given port is "up". 740369d34b8SJeremy Spewock 741369d34b8SJeremy Spewock Arguments: 742369d34b8SJeremy Spewock port_id: Port to check the link status on. 743369d34b8SJeremy Spewock timeout: Time to wait for the link to come up. The default value for this 744369d34b8SJeremy Spewock argument may be modified using the :option:`--timeout` command-line argument 745369d34b8SJeremy Spewock or the :envvar:`DTS_TIMEOUT` environment variable. 746369d34b8SJeremy Spewock 747369d34b8SJeremy Spewock Returns: 748369d34b8SJeremy Spewock Whether the link came up in time or not. 749369d34b8SJeremy Spewock """ 750369d34b8SJeremy Spewock time_to_stop = time.time() + timeout 751369d34b8SJeremy Spewock port_info: str = "" 752369d34b8SJeremy Spewock while time.time() < time_to_stop: 753369d34b8SJeremy Spewock port_info = self.send_command(f"show port info {port_id}") 754369d34b8SJeremy Spewock if "Link status: up" in port_info: 755369d34b8SJeremy Spewock break 756369d34b8SJeremy Spewock time.sleep(0.5) 757369d34b8SJeremy Spewock else: 758369d34b8SJeremy Spewock self._logger.error(f"The link for port {port_id} did not come up in the given timeout.") 759369d34b8SJeremy Spewock return "Link status: up" in port_info 760369d34b8SJeremy Spewock 761fc0f7dc4SLuca Vizzarro def set_forward_mode(self, mode: SimpleForwardingModes, verify: bool = True): 762369d34b8SJeremy Spewock """Set packet forwarding mode. 763369d34b8SJeremy Spewock 764369d34b8SJeremy Spewock Args: 765369d34b8SJeremy Spewock mode: The forwarding mode to use. 766369d34b8SJeremy Spewock verify: If :data:`True` the output of the command will be scanned in an attempt to 767369d34b8SJeremy Spewock verify that the forwarding mode was set to `mode` properly. 768369d34b8SJeremy Spewock 769369d34b8SJeremy Spewock Raises: 770369d34b8SJeremy Spewock InteractiveCommandExecutionError: If `verify` is :data:`True` and the forwarding mode 771369d34b8SJeremy Spewock fails to update. 772369d34b8SJeremy Spewock """ 773369d34b8SJeremy Spewock set_fwd_output = self.send_command(f"set fwd {mode.value}") 774369d34b8SJeremy Spewock if f"Set {mode.value} packet forwarding mode" not in set_fwd_output: 775369d34b8SJeremy Spewock self._logger.debug(f"Failed to set fwd mode to {mode.value}:\n{set_fwd_output}") 776369d34b8SJeremy Spewock raise InteractiveCommandExecutionError( 777369d34b8SJeremy Spewock f"Test pmd failed to set fwd mode to {mode.value}" 778369d34b8SJeremy Spewock ) 779369d34b8SJeremy Spewock 7802ecfd126SLuca Vizzarro def stop_all_ports(self, verify: bool = True) -> None: 7812ecfd126SLuca Vizzarro """Stops all the ports. 7822ecfd126SLuca Vizzarro 7832ecfd126SLuca Vizzarro Args: 7842ecfd126SLuca Vizzarro verify: If :data:`True`, the output of the command will be checked for a successful 7852ecfd126SLuca Vizzarro execution. 7862ecfd126SLuca Vizzarro 7872ecfd126SLuca Vizzarro Raises: 7882ecfd126SLuca Vizzarro InteractiveCommandExecutionError: If `verify` is :data:`True` and the ports were not 7892ecfd126SLuca Vizzarro stopped successfully. 7902ecfd126SLuca Vizzarro """ 7912ecfd126SLuca Vizzarro self._logger.debug("Stopping all the ports...") 7922ecfd126SLuca Vizzarro output = self.send_command("port stop all") 7932ecfd126SLuca Vizzarro if verify and not output.strip().endswith("Done"): 7942ecfd126SLuca Vizzarro raise InteractiveCommandExecutionError("Ports were not stopped successfully.") 7952ecfd126SLuca Vizzarro 7962ecfd126SLuca Vizzarro self.ports_started = False 7972ecfd126SLuca Vizzarro 7982ecfd126SLuca Vizzarro def start_all_ports(self, verify: bool = True) -> None: 7992ecfd126SLuca Vizzarro """Starts all the ports. 8002ecfd126SLuca Vizzarro 8012ecfd126SLuca Vizzarro Args: 8022ecfd126SLuca Vizzarro verify: If :data:`True`, the output of the command will be checked for a successful 8032ecfd126SLuca Vizzarro execution. 8042ecfd126SLuca Vizzarro 8052ecfd126SLuca Vizzarro Raises: 8062ecfd126SLuca Vizzarro InteractiveCommandExecutionError: If `verify` is :data:`True` and the ports were not 8072ecfd126SLuca Vizzarro started successfully. 8082ecfd126SLuca Vizzarro """ 8092ecfd126SLuca Vizzarro self._logger.debug("Starting all the ports...") 8102ecfd126SLuca Vizzarro output = self.send_command("port start all") 8112ecfd126SLuca Vizzarro if verify and not output.strip().endswith("Done"): 8122ecfd126SLuca Vizzarro raise InteractiveCommandExecutionError("Ports were not started successfully.") 8132ecfd126SLuca Vizzarro 8142ecfd126SLuca Vizzarro self.ports_started = True 8152ecfd126SLuca Vizzarro 81607816eadSLuca Vizzarro @requires_stopped_ports 81707816eadSLuca Vizzarro def set_ports_queues(self, number_of: int) -> None: 81807816eadSLuca Vizzarro """Sets the number of queues per port. 81907816eadSLuca Vizzarro 82007816eadSLuca Vizzarro Args: 82107816eadSLuca Vizzarro number_of: The number of RX/TX queues to create per port. 82207816eadSLuca Vizzarro 82307816eadSLuca Vizzarro Raises: 82407816eadSLuca Vizzarro InternalError: If `number_of` is invalid. 82507816eadSLuca Vizzarro """ 82607816eadSLuca Vizzarro if number_of < 1: 82707816eadSLuca Vizzarro raise InternalError("The number of queues must be positive and non-zero.") 82807816eadSLuca Vizzarro 82907816eadSLuca Vizzarro self.send_command(f"port config all rxq {number_of}") 83007816eadSLuca Vizzarro self.send_command(f"port config all txq {number_of}") 83107816eadSLuca Vizzarro 83261d5bc9bSLuca Vizzarro def show_port_info_all(self) -> list[TestPmdPort]: 83361d5bc9bSLuca Vizzarro """Returns the information of all the ports. 83461d5bc9bSLuca Vizzarro 83561d5bc9bSLuca Vizzarro Returns: 83661d5bc9bSLuca Vizzarro list[TestPmdPort]: A list containing all the ports information as `TestPmdPort`. 83761d5bc9bSLuca Vizzarro """ 83861d5bc9bSLuca Vizzarro output = self.send_command("show port info all") 83961d5bc9bSLuca Vizzarro 84061d5bc9bSLuca Vizzarro # Sample output of the "all" command looks like: 84161d5bc9bSLuca Vizzarro # 84261d5bc9bSLuca Vizzarro # <start> 84361d5bc9bSLuca Vizzarro # 84461d5bc9bSLuca Vizzarro # ********************* Infos for port 0 ********************* 84561d5bc9bSLuca Vizzarro # Key: value 84661d5bc9bSLuca Vizzarro # 84761d5bc9bSLuca Vizzarro # ********************* Infos for port 1 ********************* 84861d5bc9bSLuca Vizzarro # Key: value 84961d5bc9bSLuca Vizzarro # <end> 85061d5bc9bSLuca Vizzarro # 85161d5bc9bSLuca Vizzarro # Takes advantage of the double new line in between ports as end delimiter. But we need to 85261d5bc9bSLuca Vizzarro # artificially add a new line at the end to pick up the last port. Because commands are 85361d5bc9bSLuca Vizzarro # executed on a pseudo-terminal created by paramiko on the remote node, lines end with CRLF. 85461d5bc9bSLuca Vizzarro # Therefore we also need to take the carriage return into account. 85561d5bc9bSLuca Vizzarro iter = re.finditer(r"\*{21}.*?[\r\n]{4}", output + "\r\n", re.S) 85661d5bc9bSLuca Vizzarro return [TestPmdPort.parse(block.group(0)) for block in iter] 85761d5bc9bSLuca Vizzarro 85861d5bc9bSLuca Vizzarro def show_port_info(self, port_id: int) -> TestPmdPort: 85961d5bc9bSLuca Vizzarro """Returns the given port information. 86061d5bc9bSLuca Vizzarro 86161d5bc9bSLuca Vizzarro Args: 86261d5bc9bSLuca Vizzarro port_id: The port ID to gather information for. 86361d5bc9bSLuca Vizzarro 86461d5bc9bSLuca Vizzarro Raises: 86561d5bc9bSLuca Vizzarro InteractiveCommandExecutionError: If `port_id` is invalid. 86661d5bc9bSLuca Vizzarro 86761d5bc9bSLuca Vizzarro Returns: 86861d5bc9bSLuca Vizzarro TestPmdPort: An instance of `TestPmdPort` containing the given port's information. 86961d5bc9bSLuca Vizzarro """ 87061d5bc9bSLuca Vizzarro output = self.send_command(f"show port info {port_id}", skip_first_line=True) 87161d5bc9bSLuca Vizzarro if output.startswith("Invalid port"): 87261d5bc9bSLuca Vizzarro raise InteractiveCommandExecutionError("invalid port given") 87361d5bc9bSLuca Vizzarro 87461d5bc9bSLuca Vizzarro return TestPmdPort.parse(output) 87561d5bc9bSLuca Vizzarro 87653eacf3dSLuca Vizzarro def show_port_stats_all(self) -> list[TestPmdPortStats]: 87753eacf3dSLuca Vizzarro """Returns the statistics of all the ports. 87853eacf3dSLuca Vizzarro 87953eacf3dSLuca Vizzarro Returns: 88053eacf3dSLuca Vizzarro list[TestPmdPortStats]: A list containing all the ports stats as `TestPmdPortStats`. 88153eacf3dSLuca Vizzarro """ 88253eacf3dSLuca Vizzarro output = self.send_command("show port stats all") 88353eacf3dSLuca Vizzarro 88453eacf3dSLuca Vizzarro # Sample output of the "all" command looks like: 88553eacf3dSLuca Vizzarro # 88653eacf3dSLuca Vizzarro # ########### NIC statistics for port 0 ########### 88753eacf3dSLuca Vizzarro # values... 88853eacf3dSLuca Vizzarro # ################################################# 88953eacf3dSLuca Vizzarro # 89053eacf3dSLuca Vizzarro # ########### NIC statistics for port 1 ########### 89153eacf3dSLuca Vizzarro # values... 89253eacf3dSLuca Vizzarro # ################################################# 89353eacf3dSLuca Vizzarro # 89453eacf3dSLuca Vizzarro iter = re.finditer(r"(^ #*.+#*$[^#]+)^ #*\r$", output, re.MULTILINE) 89553eacf3dSLuca Vizzarro return [TestPmdPortStats.parse(block.group(1)) for block in iter] 89653eacf3dSLuca Vizzarro 89753eacf3dSLuca Vizzarro def show_port_stats(self, port_id: int) -> TestPmdPortStats: 89853eacf3dSLuca Vizzarro """Returns the given port statistics. 89953eacf3dSLuca Vizzarro 90053eacf3dSLuca Vizzarro Args: 90153eacf3dSLuca Vizzarro port_id: The port ID to gather information for. 90253eacf3dSLuca Vizzarro 90353eacf3dSLuca Vizzarro Raises: 90453eacf3dSLuca Vizzarro InteractiveCommandExecutionError: If `port_id` is invalid. 90553eacf3dSLuca Vizzarro 90653eacf3dSLuca Vizzarro Returns: 90753eacf3dSLuca Vizzarro TestPmdPortStats: An instance of `TestPmdPortStats` containing the given port's stats. 90853eacf3dSLuca Vizzarro """ 90953eacf3dSLuca Vizzarro output = self.send_command(f"show port stats {port_id}", skip_first_line=True) 91053eacf3dSLuca Vizzarro if output.startswith("Invalid port"): 91153eacf3dSLuca Vizzarro raise InteractiveCommandExecutionError("invalid port given") 91253eacf3dSLuca Vizzarro 91353eacf3dSLuca Vizzarro return TestPmdPortStats.parse(output) 91453eacf3dSLuca Vizzarro 9152b648cd4SJeremy Spewock def _close(self) -> None: 916369d34b8SJeremy Spewock """Overrides :meth:`~.interactive_shell.close`.""" 9172b648cd4SJeremy Spewock self.stop() 91892439dc9SJeremy Spewock self.send_command("quit", "Bye...") 9192b648cd4SJeremy Spewock return super()._close() 920