xref: /dpdk/dts/framework/remote_session/testpmd_shell.py (revision bee7cf823cd80f83b026a02508ba05fdac1b4113)
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
20c89d0038SJuraj Linkešfrom collections.abc import Callable, MutableSet
2161d5bc9bSLuca Vizzarrofrom dataclasses import dataclass, field
2261d5bc9bSLuca Vizzarrofrom enum import Flag, auto
23949d1488SLuca Vizzarrofrom os import environ
24840b1e01SJuraj Linkešfrom pathlib import PurePath
25c89d0038SJuraj Linkešfrom typing import TYPE_CHECKING, Any, ClassVar, Concatenate, ParamSpec, TypeAlias
26c89d0038SJuraj Linkeš
27949d1488SLuca Vizzarroif TYPE_CHECKING or environ.get("DTS_DOC_BUILD"):
28c89d0038SJuraj Linkeš    from enum import Enum as NoAliasEnum
29c89d0038SJuraj Linkešelse:
30c89d0038SJuraj Linkeš    from aenum import NoAliasEnum
31840b1e01SJuraj Linkeš
3287ba4cdcSLuca Vizzarrofrom typing_extensions import Self, Unpack
3361d5bc9bSLuca Vizzarro
342ecfd126SLuca Vizzarrofrom framework.exception import InteractiveCommandExecutionError, InternalError
35fc0f7dc4SLuca Vizzarrofrom framework.params.testpmd import SimpleForwardingModes, TestPmdParams
3687ba4cdcSLuca Vizzarrofrom framework.params.types import TestPmdParamsDict
3761d5bc9bSLuca Vizzarrofrom framework.parser import ParserFn, TextParser
38bfad0948SLuca Vizzarrofrom framework.remote_session.dpdk_shell import DPDKShell
39369d34b8SJeremy Spewockfrom framework.settings import SETTINGS
40bfad0948SLuca Vizzarrofrom framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
41bfad0948SLuca Vizzarrofrom framework.testbed_model.sut_node import SutNode
42a91d5f47SJeremy Spewockfrom framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
43369d34b8SJeremy Spewock
442ecfd126SLuca VizzarroP = ParamSpec("P")
452ecfd126SLuca VizzarroTestPmdShellMethod = Callable[Concatenate["TestPmdShell", P], Any]
462ecfd126SLuca Vizzarro
47c89d0038SJuraj LinkešTestPmdShellCapabilityMethod: TypeAlias = Callable[
48c89d0038SJuraj Linkeš    ["TestPmdShell", MutableSet["NicCapability"], MutableSet["NicCapability"]], None
49c89d0038SJuraj Linkeš]
50c89d0038SJuraj Linkeš
51c89d0038SJuraj LinkešTestPmdShellDecorator: TypeAlias = Callable[[TestPmdShellMethod], TestPmdShellMethod]
52c89d0038SJuraj Linkeš
53c0119400SJuraj LinkešTestPmdShellNicCapability = (
54c0119400SJuraj Linkeš    TestPmdShellCapabilityMethod | tuple[TestPmdShellCapabilityMethod, TestPmdShellDecorator]
55c0119400SJuraj Linkeš)
56c0119400SJuraj Linkeš
57840b1e01SJuraj Linkeš
583e967643SJuraj Linkešclass TestPmdDevice:
596ef07151SJuraj Linkeš    """The data of a device that testpmd can recognize.
606ef07151SJuraj Linkeš
616ef07151SJuraj Linkeš    Attributes:
626ef07151SJuraj Linkeš        pci_address: The PCI address of the device.
636ef07151SJuraj Linkeš    """
646ef07151SJuraj Linkeš
65840b1e01SJuraj Linkeš    pci_address: str
66840b1e01SJuraj Linkeš
67840b1e01SJuraj Linkeš    def __init__(self, pci_address_line: str):
686ef07151SJuraj Linkeš        """Initialize the device from the testpmd output line string.
696ef07151SJuraj Linkeš
706ef07151SJuraj Linkeš        Args:
716ef07151SJuraj Linkeš            pci_address_line: A line of testpmd output that contains a device.
726ef07151SJuraj Linkeš        """
73840b1e01SJuraj Linkeš        self.pci_address = pci_address_line.strip().split(": ")[1].strip()
74840b1e01SJuraj Linkeš
75840b1e01SJuraj Linkeš    def __str__(self) -> str:
766ef07151SJuraj Linkeš        """The PCI address captures what the device is."""
77840b1e01SJuraj Linkeš        return self.pci_address
78840b1e01SJuraj Linkeš
79840b1e01SJuraj Linkeš
8061d5bc9bSLuca Vizzarroclass VLANOffloadFlag(Flag):
8161d5bc9bSLuca Vizzarro    """Flag representing the VLAN offload settings of a NIC port."""
8261d5bc9bSLuca Vizzarro
8361d5bc9bSLuca Vizzarro    #:
8461d5bc9bSLuca Vizzarro    STRIP = auto()
8561d5bc9bSLuca Vizzarro    #:
8661d5bc9bSLuca Vizzarro    FILTER = auto()
8761d5bc9bSLuca Vizzarro    #:
8861d5bc9bSLuca Vizzarro    EXTEND = auto()
8961d5bc9bSLuca Vizzarro    #:
9061d5bc9bSLuca Vizzarro    QINQ_STRIP = auto()
9161d5bc9bSLuca Vizzarro
9261d5bc9bSLuca Vizzarro    @classmethod
9361d5bc9bSLuca Vizzarro    def from_str_dict(cls, d):
9461d5bc9bSLuca Vizzarro        """Makes an instance from a dict containing the flag member names with an "on" value.
9561d5bc9bSLuca Vizzarro
9661d5bc9bSLuca Vizzarro        Args:
9761d5bc9bSLuca Vizzarro            d: A dictionary containing the flag members as keys and any string value.
9861d5bc9bSLuca Vizzarro
9961d5bc9bSLuca Vizzarro        Returns:
10061d5bc9bSLuca Vizzarro            A new instance of the flag.
10161d5bc9bSLuca Vizzarro        """
10261d5bc9bSLuca Vizzarro        flag = cls(0)
10361d5bc9bSLuca Vizzarro        for name in cls.__members__:
10461d5bc9bSLuca Vizzarro            if d.get(name) == "on":
10561d5bc9bSLuca Vizzarro                flag |= cls[name]
10661d5bc9bSLuca Vizzarro        return flag
10761d5bc9bSLuca Vizzarro
10861d5bc9bSLuca Vizzarro    @classmethod
10961d5bc9bSLuca Vizzarro    def make_parser(cls) -> ParserFn:
11061d5bc9bSLuca Vizzarro        """Makes a parser function.
11161d5bc9bSLuca Vizzarro
11261d5bc9bSLuca Vizzarro        Returns:
11361d5bc9bSLuca Vizzarro            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a
11461d5bc9bSLuca Vizzarro                parser function that makes an instance of this flag from text.
11561d5bc9bSLuca Vizzarro        """
11661d5bc9bSLuca Vizzarro        return TextParser.wrap(
11761d5bc9bSLuca Vizzarro            TextParser.find(
11861d5bc9bSLuca Vizzarro                r"VLAN offload:\s+"
11961d5bc9bSLuca Vizzarro                r"strip (?P<STRIP>on|off), "
12061d5bc9bSLuca Vizzarro                r"filter (?P<FILTER>on|off), "
12161d5bc9bSLuca Vizzarro                r"extend (?P<EXTEND>on|off), "
1221a7520a1SDean Marx                r"qinq strip (?P<QINQ_STRIP>on|off)",
12361d5bc9bSLuca Vizzarro                re.MULTILINE,
12461d5bc9bSLuca Vizzarro                named=True,
12561d5bc9bSLuca Vizzarro            ),
12661d5bc9bSLuca Vizzarro            cls.from_str_dict,
12761d5bc9bSLuca Vizzarro        )
12861d5bc9bSLuca Vizzarro
12961d5bc9bSLuca Vizzarro
130*bee7cf82SDean Marxclass ChecksumOffloadOptions(Flag):
131*bee7cf82SDean Marx    """Flag representing checksum hardware offload layer options."""
132*bee7cf82SDean Marx
133*bee7cf82SDean Marx    #:
134*bee7cf82SDean Marx    ip = auto()
135*bee7cf82SDean Marx    #:
136*bee7cf82SDean Marx    udp = auto()
137*bee7cf82SDean Marx    #:
138*bee7cf82SDean Marx    tcp = auto()
139*bee7cf82SDean Marx    #:
140*bee7cf82SDean Marx    sctp = auto()
141*bee7cf82SDean Marx    #:
142*bee7cf82SDean Marx    outer_ip = auto()
143*bee7cf82SDean Marx    #:
144*bee7cf82SDean Marx    outer_udp = auto()
145*bee7cf82SDean Marx
146*bee7cf82SDean Marx
14761d5bc9bSLuca Vizzarroclass RSSOffloadTypesFlag(Flag):
14861d5bc9bSLuca Vizzarro    """Flag representing the RSS offload flow types supported by the NIC port."""
14961d5bc9bSLuca Vizzarro
15061d5bc9bSLuca Vizzarro    #:
15161d5bc9bSLuca Vizzarro    ipv4 = auto()
15261d5bc9bSLuca Vizzarro    #:
15361d5bc9bSLuca Vizzarro    ipv4_frag = auto()
15461d5bc9bSLuca Vizzarro    #:
15561d5bc9bSLuca Vizzarro    ipv4_tcp = auto()
15661d5bc9bSLuca Vizzarro    #:
15761d5bc9bSLuca Vizzarro    ipv4_udp = auto()
15861d5bc9bSLuca Vizzarro    #:
15961d5bc9bSLuca Vizzarro    ipv4_sctp = auto()
16061d5bc9bSLuca Vizzarro    #:
16161d5bc9bSLuca Vizzarro    ipv4_other = auto()
16261d5bc9bSLuca Vizzarro    #:
16361d5bc9bSLuca Vizzarro    ipv6 = auto()
16461d5bc9bSLuca Vizzarro    #:
16561d5bc9bSLuca Vizzarro    ipv6_frag = auto()
16661d5bc9bSLuca Vizzarro    #:
16761d5bc9bSLuca Vizzarro    ipv6_tcp = auto()
16861d5bc9bSLuca Vizzarro    #:
16961d5bc9bSLuca Vizzarro    ipv6_udp = auto()
17061d5bc9bSLuca Vizzarro    #:
17161d5bc9bSLuca Vizzarro    ipv6_sctp = auto()
17261d5bc9bSLuca Vizzarro    #:
17361d5bc9bSLuca Vizzarro    ipv6_other = auto()
17461d5bc9bSLuca Vizzarro    #:
17561d5bc9bSLuca Vizzarro    l2_payload = auto()
17661d5bc9bSLuca Vizzarro    #:
17761d5bc9bSLuca Vizzarro    ipv6_ex = auto()
17861d5bc9bSLuca Vizzarro    #:
17961d5bc9bSLuca Vizzarro    ipv6_tcp_ex = auto()
18061d5bc9bSLuca Vizzarro    #:
18161d5bc9bSLuca Vizzarro    ipv6_udp_ex = auto()
18261d5bc9bSLuca Vizzarro    #:
18361d5bc9bSLuca Vizzarro    port = auto()
18461d5bc9bSLuca Vizzarro    #:
18561d5bc9bSLuca Vizzarro    vxlan = auto()
18661d5bc9bSLuca Vizzarro    #:
18761d5bc9bSLuca Vizzarro    geneve = auto()
18861d5bc9bSLuca Vizzarro    #:
18961d5bc9bSLuca Vizzarro    nvgre = auto()
19061d5bc9bSLuca Vizzarro    #:
19161d5bc9bSLuca Vizzarro    user_defined_22 = auto()
19261d5bc9bSLuca Vizzarro    #:
19361d5bc9bSLuca Vizzarro    gtpu = auto()
19461d5bc9bSLuca Vizzarro    #:
19561d5bc9bSLuca Vizzarro    eth = auto()
19661d5bc9bSLuca Vizzarro    #:
19761d5bc9bSLuca Vizzarro    s_vlan = auto()
19861d5bc9bSLuca Vizzarro    #:
19961d5bc9bSLuca Vizzarro    c_vlan = auto()
20061d5bc9bSLuca Vizzarro    #:
20161d5bc9bSLuca Vizzarro    esp = auto()
20261d5bc9bSLuca Vizzarro    #:
20361d5bc9bSLuca Vizzarro    ah = auto()
20461d5bc9bSLuca Vizzarro    #:
20561d5bc9bSLuca Vizzarro    l2tpv3 = auto()
20661d5bc9bSLuca Vizzarro    #:
20761d5bc9bSLuca Vizzarro    pfcp = auto()
20861d5bc9bSLuca Vizzarro    #:
20961d5bc9bSLuca Vizzarro    pppoe = auto()
21061d5bc9bSLuca Vizzarro    #:
21161d5bc9bSLuca Vizzarro    ecpri = auto()
21261d5bc9bSLuca Vizzarro    #:
21361d5bc9bSLuca Vizzarro    mpls = auto()
21461d5bc9bSLuca Vizzarro    #:
21561d5bc9bSLuca Vizzarro    ipv4_chksum = auto()
21661d5bc9bSLuca Vizzarro    #:
21761d5bc9bSLuca Vizzarro    l4_chksum = auto()
21861d5bc9bSLuca Vizzarro    #:
21961d5bc9bSLuca Vizzarro    l2tpv2 = auto()
22061d5bc9bSLuca Vizzarro    #:
22161d5bc9bSLuca Vizzarro    ipv6_flow_label = auto()
22261d5bc9bSLuca Vizzarro    #:
22361d5bc9bSLuca Vizzarro    user_defined_38 = auto()
22461d5bc9bSLuca Vizzarro    #:
22561d5bc9bSLuca Vizzarro    user_defined_39 = auto()
22661d5bc9bSLuca Vizzarro    #:
22761d5bc9bSLuca Vizzarro    user_defined_40 = auto()
22861d5bc9bSLuca Vizzarro    #:
22961d5bc9bSLuca Vizzarro    user_defined_41 = auto()
23061d5bc9bSLuca Vizzarro    #:
23161d5bc9bSLuca Vizzarro    user_defined_42 = auto()
23261d5bc9bSLuca Vizzarro    #:
23361d5bc9bSLuca Vizzarro    user_defined_43 = auto()
23461d5bc9bSLuca Vizzarro    #:
23561d5bc9bSLuca Vizzarro    user_defined_44 = auto()
23661d5bc9bSLuca Vizzarro    #:
23761d5bc9bSLuca Vizzarro    user_defined_45 = auto()
23861d5bc9bSLuca Vizzarro    #:
23961d5bc9bSLuca Vizzarro    user_defined_46 = auto()
24061d5bc9bSLuca Vizzarro    #:
24161d5bc9bSLuca Vizzarro    user_defined_47 = auto()
24261d5bc9bSLuca Vizzarro    #:
24361d5bc9bSLuca Vizzarro    user_defined_48 = auto()
24461d5bc9bSLuca Vizzarro    #:
24561d5bc9bSLuca Vizzarro    user_defined_49 = auto()
24661d5bc9bSLuca Vizzarro    #:
24761d5bc9bSLuca Vizzarro    user_defined_50 = auto()
24861d5bc9bSLuca Vizzarro    #:
24961d5bc9bSLuca Vizzarro    user_defined_51 = auto()
25061d5bc9bSLuca Vizzarro    #:
25161d5bc9bSLuca Vizzarro    l3_pre96 = auto()
25261d5bc9bSLuca Vizzarro    #:
25361d5bc9bSLuca Vizzarro    l3_pre64 = auto()
25461d5bc9bSLuca Vizzarro    #:
25561d5bc9bSLuca Vizzarro    l3_pre56 = auto()
25661d5bc9bSLuca Vizzarro    #:
25761d5bc9bSLuca Vizzarro    l3_pre48 = auto()
25861d5bc9bSLuca Vizzarro    #:
25961d5bc9bSLuca Vizzarro    l3_pre40 = auto()
26061d5bc9bSLuca Vizzarro    #:
26161d5bc9bSLuca Vizzarro    l3_pre32 = auto()
26261d5bc9bSLuca Vizzarro    #:
26361d5bc9bSLuca Vizzarro    l2_dst_only = auto()
26461d5bc9bSLuca Vizzarro    #:
26561d5bc9bSLuca Vizzarro    l2_src_only = auto()
26661d5bc9bSLuca Vizzarro    #:
26761d5bc9bSLuca Vizzarro    l4_dst_only = auto()
26861d5bc9bSLuca Vizzarro    #:
26961d5bc9bSLuca Vizzarro    l4_src_only = auto()
27061d5bc9bSLuca Vizzarro    #:
27161d5bc9bSLuca Vizzarro    l3_dst_only = auto()
27261d5bc9bSLuca Vizzarro    #:
27361d5bc9bSLuca Vizzarro    l3_src_only = auto()
27461d5bc9bSLuca Vizzarro
27561d5bc9bSLuca Vizzarro    #:
27661d5bc9bSLuca Vizzarro    ip = ipv4 | ipv4_frag | ipv4_other | ipv6 | ipv6_frag | ipv6_other | ipv6_ex
27761d5bc9bSLuca Vizzarro    #:
27861d5bc9bSLuca Vizzarro    udp = ipv4_udp | ipv6_udp | ipv6_udp_ex
27961d5bc9bSLuca Vizzarro    #:
28061d5bc9bSLuca Vizzarro    tcp = ipv4_tcp | ipv6_tcp | ipv6_tcp_ex
28161d5bc9bSLuca Vizzarro    #:
28261d5bc9bSLuca Vizzarro    sctp = ipv4_sctp | ipv6_sctp
28361d5bc9bSLuca Vizzarro    #:
28461d5bc9bSLuca Vizzarro    tunnel = vxlan | geneve | nvgre
28561d5bc9bSLuca Vizzarro    #:
28661d5bc9bSLuca Vizzarro    vlan = s_vlan | c_vlan
28761d5bc9bSLuca Vizzarro    #:
28861d5bc9bSLuca Vizzarro    all = (
28961d5bc9bSLuca Vizzarro        eth
29061d5bc9bSLuca Vizzarro        | vlan
29161d5bc9bSLuca Vizzarro        | ip
29261d5bc9bSLuca Vizzarro        | tcp
29361d5bc9bSLuca Vizzarro        | udp
29461d5bc9bSLuca Vizzarro        | sctp
29561d5bc9bSLuca Vizzarro        | l2_payload
29661d5bc9bSLuca Vizzarro        | l2tpv3
29761d5bc9bSLuca Vizzarro        | esp
29861d5bc9bSLuca Vizzarro        | ah
29961d5bc9bSLuca Vizzarro        | pfcp
30061d5bc9bSLuca Vizzarro        | gtpu
30161d5bc9bSLuca Vizzarro        | ecpri
30261d5bc9bSLuca Vizzarro        | mpls
30361d5bc9bSLuca Vizzarro        | l2tpv2
30461d5bc9bSLuca Vizzarro    )
30561d5bc9bSLuca Vizzarro
30661d5bc9bSLuca Vizzarro    @classmethod
30761d5bc9bSLuca Vizzarro    def from_list_string(cls, names: str) -> Self:
30861d5bc9bSLuca Vizzarro        """Makes a flag from a whitespace-separated list of names.
30961d5bc9bSLuca Vizzarro
31061d5bc9bSLuca Vizzarro        Args:
31161d5bc9bSLuca Vizzarro            names: a whitespace-separated list containing the members of this flag.
31261d5bc9bSLuca Vizzarro
31361d5bc9bSLuca Vizzarro        Returns:
31461d5bc9bSLuca Vizzarro            An instance of this flag.
31561d5bc9bSLuca Vizzarro        """
31661d5bc9bSLuca Vizzarro        flag = cls(0)
31761d5bc9bSLuca Vizzarro        for name in names.split():
31861d5bc9bSLuca Vizzarro            flag |= cls.from_str(name)
31961d5bc9bSLuca Vizzarro        return flag
32061d5bc9bSLuca Vizzarro
32161d5bc9bSLuca Vizzarro    @classmethod
32261d5bc9bSLuca Vizzarro    def from_str(cls, name: str) -> Self:
32361d5bc9bSLuca Vizzarro        """Makes a flag matching the supplied name.
32461d5bc9bSLuca Vizzarro
32561d5bc9bSLuca Vizzarro        Args:
32661d5bc9bSLuca Vizzarro            name: a valid member of this flag in text
32761d5bc9bSLuca Vizzarro        Returns:
32861d5bc9bSLuca Vizzarro            An instance of this flag.
32961d5bc9bSLuca Vizzarro        """
33061d5bc9bSLuca Vizzarro        member_name = name.strip().replace("-", "_")
33161d5bc9bSLuca Vizzarro        return cls[member_name]
33261d5bc9bSLuca Vizzarro
33361d5bc9bSLuca Vizzarro    @classmethod
33461d5bc9bSLuca Vizzarro    def make_parser(cls) -> ParserFn:
33561d5bc9bSLuca Vizzarro        """Makes a parser function.
33661d5bc9bSLuca Vizzarro
33761d5bc9bSLuca Vizzarro        Returns:
33861d5bc9bSLuca Vizzarro            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a
33961d5bc9bSLuca Vizzarro                parser function that makes an instance of this flag from text.
34061d5bc9bSLuca Vizzarro        """
34161d5bc9bSLuca Vizzarro        return TextParser.wrap(
34261d5bc9bSLuca Vizzarro            TextParser.find(r"Supported RSS offload flow types:((?:\r?\n?  \S+)+)", re.MULTILINE),
34361d5bc9bSLuca Vizzarro            RSSOffloadTypesFlag.from_list_string,
34461d5bc9bSLuca Vizzarro        )
34561d5bc9bSLuca Vizzarro
34661d5bc9bSLuca Vizzarro
34761d5bc9bSLuca Vizzarroclass DeviceCapabilitiesFlag(Flag):
34861d5bc9bSLuca Vizzarro    """Flag representing the device capabilities."""
34961d5bc9bSLuca Vizzarro
35061d5bc9bSLuca Vizzarro    #: Device supports Rx queue setup after device started.
35161d5bc9bSLuca Vizzarro    RUNTIME_RX_QUEUE_SETUP = auto()
35261d5bc9bSLuca Vizzarro    #: Device supports Tx queue setup after device started.
35361d5bc9bSLuca Vizzarro    RUNTIME_TX_QUEUE_SETUP = auto()
35461d5bc9bSLuca Vizzarro    #: Device supports shared Rx queue among ports within Rx domain and switch domain.
35561d5bc9bSLuca Vizzarro    RXQ_SHARE = auto()
35661d5bc9bSLuca Vizzarro    #: Device supports keeping flow rules across restart.
35761d5bc9bSLuca Vizzarro    FLOW_RULE_KEEP = auto()
35861d5bc9bSLuca Vizzarro    #: Device supports keeping shared flow objects across restart.
35961d5bc9bSLuca Vizzarro    FLOW_SHARED_OBJECT_KEEP = auto()
36061d5bc9bSLuca Vizzarro
36161d5bc9bSLuca Vizzarro    @classmethod
36261d5bc9bSLuca Vizzarro    def make_parser(cls) -> ParserFn:
36361d5bc9bSLuca Vizzarro        """Makes a parser function.
36461d5bc9bSLuca Vizzarro
36561d5bc9bSLuca Vizzarro        Returns:
36661d5bc9bSLuca Vizzarro            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a
36761d5bc9bSLuca Vizzarro                parser function that makes an instance of this flag from text.
36861d5bc9bSLuca Vizzarro        """
36961d5bc9bSLuca Vizzarro        return TextParser.wrap(
37061d5bc9bSLuca Vizzarro            TextParser.find_int(r"Device capabilities: (0x[A-Fa-f\d]+)"),
37161d5bc9bSLuca Vizzarro            cls,
37261d5bc9bSLuca Vizzarro        )
37361d5bc9bSLuca Vizzarro
37461d5bc9bSLuca Vizzarro
37561d5bc9bSLuca Vizzarroclass DeviceErrorHandlingMode(StrEnum):
37661d5bc9bSLuca Vizzarro    """Enum representing the device error handling mode."""
37761d5bc9bSLuca Vizzarro
37861d5bc9bSLuca Vizzarro    #:
37961d5bc9bSLuca Vizzarro    none = auto()
38061d5bc9bSLuca Vizzarro    #:
38161d5bc9bSLuca Vizzarro    passive = auto()
38261d5bc9bSLuca Vizzarro    #:
38361d5bc9bSLuca Vizzarro    proactive = auto()
38461d5bc9bSLuca Vizzarro    #:
38561d5bc9bSLuca Vizzarro    unknown = auto()
38661d5bc9bSLuca Vizzarro
38761d5bc9bSLuca Vizzarro    @classmethod
38861d5bc9bSLuca Vizzarro    def make_parser(cls) -> ParserFn:
38961d5bc9bSLuca Vizzarro        """Makes a parser function.
39061d5bc9bSLuca Vizzarro
39161d5bc9bSLuca Vizzarro        Returns:
39261d5bc9bSLuca Vizzarro            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a
39361d5bc9bSLuca Vizzarro                parser function that makes an instance of this enum from text.
39461d5bc9bSLuca Vizzarro        """
39561d5bc9bSLuca Vizzarro        return TextParser.wrap(TextParser.find(r"Device error handling mode: (\w+)"), cls)
39661d5bc9bSLuca Vizzarro
39761d5bc9bSLuca Vizzarro
39861d5bc9bSLuca Vizzarrodef make_device_private_info_parser() -> ParserFn:
39961d5bc9bSLuca Vizzarro    """Device private information parser.
40061d5bc9bSLuca Vizzarro
40161d5bc9bSLuca Vizzarro    Ensures that we are not parsing invalid device private info output.
40261d5bc9bSLuca Vizzarro
40361d5bc9bSLuca Vizzarro    Returns:
40461d5bc9bSLuca Vizzarro        ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a parser
40561d5bc9bSLuca Vizzarro            function that parses the device private info from the TestPmd port info output.
40661d5bc9bSLuca Vizzarro    """
40761d5bc9bSLuca Vizzarro
40861d5bc9bSLuca Vizzarro    def _validate(info: str):
40961d5bc9bSLuca Vizzarro        info = info.strip()
41061d5bc9bSLuca Vizzarro        if info == "none" or info.startswith("Invalid file") or info.startswith("Failed to dump"):
41161d5bc9bSLuca Vizzarro            return None
41261d5bc9bSLuca Vizzarro        return info
41361d5bc9bSLuca Vizzarro
41461d5bc9bSLuca Vizzarro    return TextParser.wrap(TextParser.find(r"Device private info:\s+([\s\S]+)"), _validate)
41561d5bc9bSLuca Vizzarro
41661d5bc9bSLuca Vizzarro
417c0119400SJuraj Linkešclass RxQueueState(StrEnum):
418c0119400SJuraj Linkeš    """RX queue states.
419c0119400SJuraj Linkeš
420c0119400SJuraj Linkeš    References:
421c0119400SJuraj Linkeš        DPDK lib: ``lib/ethdev/rte_ethdev.h``
422c0119400SJuraj Linkeš        testpmd display function: ``app/test-pmd/config.c:get_queue_state_name()``
423c0119400SJuraj Linkeš    """
424c0119400SJuraj Linkeš
425c0119400SJuraj Linkeš    #:
426c0119400SJuraj Linkeš    stopped = auto()
427c0119400SJuraj Linkeš    #:
428c0119400SJuraj Linkeš    started = auto()
429c0119400SJuraj Linkeš    #:
430c0119400SJuraj Linkeš    hairpin = auto()
431c0119400SJuraj Linkeš    #:
432c0119400SJuraj Linkeš    unknown = auto()
433c0119400SJuraj Linkeš
434c0119400SJuraj Linkeš    @classmethod
435c0119400SJuraj Linkeš    def make_parser(cls) -> ParserFn:
436c0119400SJuraj Linkeš        """Makes a parser function.
437c0119400SJuraj Linkeš
438c0119400SJuraj Linkeš        Returns:
439c0119400SJuraj Linkeš            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a
440c0119400SJuraj Linkeš                parser function that makes an instance of this enum from text.
441c0119400SJuraj Linkeš        """
442c0119400SJuraj Linkeš        return TextParser.wrap(TextParser.find(r"Rx queue state: ([^\r\n]+)"), cls)
443c0119400SJuraj Linkeš
444c0119400SJuraj Linkeš
445c0119400SJuraj Linkeš@dataclass
446c986c339SDean Marxclass TestPmdQueueInfo(TextParser):
447c986c339SDean Marx    """Dataclass representation of the common parts of the testpmd `show rxq/txq info` commands."""
448c986c339SDean Marx
449c986c339SDean Marx    #:
450c986c339SDean Marx    prefetch_threshold: int = field(metadata=TextParser.find_int(r"prefetch threshold: (\d+)"))
451c986c339SDean Marx    #:
452c986c339SDean Marx    host_threshold: int = field(metadata=TextParser.find_int(r"host threshold: (\d+)"))
453c986c339SDean Marx    #:
454c986c339SDean Marx    writeback_threshold: int = field(metadata=TextParser.find_int(r"writeback threshold: (\d+)"))
455c986c339SDean Marx    #:
456c986c339SDean Marx    free_threshold: int = field(metadata=TextParser.find_int(r"free threshold: (\d+)"))
457c986c339SDean Marx    #:
458c986c339SDean Marx    deferred_start: bool = field(metadata=TextParser.find("deferred start: on"))
459c986c339SDean Marx    #: The number of RXD/TXDs is just the ring size of the queue.
460c986c339SDean Marx    ring_size: int = field(metadata=TextParser.find_int(r"Number of (?:RXDs|TXDs): (\d+)"))
461c986c339SDean Marx    #:
462c986c339SDean Marx    is_queue_started: bool = field(metadata=TextParser.find("queue state: started"))
463c986c339SDean Marx    #:
464c986c339SDean Marx    burst_mode: str | None = field(
465c986c339SDean Marx        default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)")
466c986c339SDean Marx    )
467c986c339SDean Marx
468c986c339SDean Marx
469c986c339SDean Marx@dataclass
470c986c339SDean Marxclass TestPmdTxqInfo(TestPmdQueueInfo):
471c986c339SDean Marx    """Representation of testpmd's ``show txq info <port_id> <queue_id>`` command.
472c986c339SDean Marx
473c986c339SDean Marx    References:
474c986c339SDean Marx        testpmd command function: ``app/test-pmd/cmdline.c:cmd_showqueue()``
475c986c339SDean Marx        testpmd display function: ``app/test-pmd/config.c:rx_queue_infos_display()``
476c986c339SDean Marx    """
477c986c339SDean Marx
478c986c339SDean Marx    #: Ring size threshold
479c986c339SDean Marx    rs_threshold: int | None = field(
480c986c339SDean Marx        default=None, metadata=TextParser.find_int(r"TX RS threshold: (\d+)\b")
481c986c339SDean Marx    )
482c986c339SDean Marx
483c986c339SDean Marx
484c986c339SDean Marx@dataclass
485c986c339SDean Marxclass TestPmdRxqInfo(TestPmdQueueInfo):
486c0119400SJuraj Linkeš    """Representation of testpmd's ``show rxq info <port_id> <queue_id>`` command.
487c0119400SJuraj Linkeš
488c0119400SJuraj Linkeš    References:
489c0119400SJuraj Linkeš        testpmd command function: ``app/test-pmd/cmdline.c:cmd_showqueue()``
490c0119400SJuraj Linkeš        testpmd display function: ``app/test-pmd/config.c:rx_queue_infos_display()``
491c0119400SJuraj Linkeš    """
492c0119400SJuraj Linkeš
493c0119400SJuraj Linkeš    #: Mempool used by that queue
494c986c339SDean Marx    mempool: str | None = field(default=None, metadata=TextParser.find(r"Mempool: ([^\r\n]+)"))
495c0119400SJuraj Linkeš    #: Drop packets if no descriptors are available
496c986c339SDean Marx    drop_packets: bool | None = field(
497c986c339SDean Marx        default=None, metadata=TextParser.find(r"RX drop packets: on")
498c986c339SDean Marx    )
499c0119400SJuraj Linkeš    #: Scattered packets Rx enabled
500c986c339SDean Marx    scattered_packets: bool | None = field(
501c986c339SDean Marx        default=None, metadata=TextParser.find(r"RX scattered packets: on")
502c986c339SDean Marx    )
503c0119400SJuraj Linkeš    #: The state of the queue
504c986c339SDean Marx    queue_state: str | None = field(default=None, metadata=RxQueueState.make_parser())
505c0119400SJuraj Linkeš
506c0119400SJuraj Linkeš
50761d5bc9bSLuca Vizzarro@dataclass
50861d5bc9bSLuca Vizzarroclass TestPmdPort(TextParser):
50961d5bc9bSLuca Vizzarro    """Dataclass representing the result of testpmd's ``show port info`` command."""
51061d5bc9bSLuca Vizzarro
51161d5bc9bSLuca Vizzarro    #:
51261d5bc9bSLuca Vizzarro    id: int = field(metadata=TextParser.find_int(r"Infos for port (\d+)\b"))
51361d5bc9bSLuca Vizzarro    #:
51461d5bc9bSLuca Vizzarro    device_name: str = field(metadata=TextParser.find(r"Device name: ([^\r\n]+)"))
51561d5bc9bSLuca Vizzarro    #:
51661d5bc9bSLuca Vizzarro    driver_name: str = field(metadata=TextParser.find(r"Driver name: ([^\r\n]+)"))
51761d5bc9bSLuca Vizzarro    #:
51861d5bc9bSLuca Vizzarro    socket_id: int = field(metadata=TextParser.find_int(r"Connect to socket: (\d+)"))
51961d5bc9bSLuca Vizzarro    #:
52061d5bc9bSLuca Vizzarro    is_link_up: bool = field(metadata=TextParser.find("Link status: up"))
52161d5bc9bSLuca Vizzarro    #:
52261d5bc9bSLuca Vizzarro    link_speed: str = field(metadata=TextParser.find(r"Link speed: ([^\r\n]+)"))
52361d5bc9bSLuca Vizzarro    #:
52461d5bc9bSLuca Vizzarro    is_link_full_duplex: bool = field(metadata=TextParser.find("Link duplex: full-duplex"))
52561d5bc9bSLuca Vizzarro    #:
52661d5bc9bSLuca Vizzarro    is_link_autonegotiated: bool = field(metadata=TextParser.find("Autoneg status: On"))
52761d5bc9bSLuca Vizzarro    #:
52861d5bc9bSLuca Vizzarro    is_promiscuous_mode_enabled: bool = field(metadata=TextParser.find("Promiscuous mode: enabled"))
52961d5bc9bSLuca Vizzarro    #:
53061d5bc9bSLuca Vizzarro    is_allmulticast_mode_enabled: bool = field(
53161d5bc9bSLuca Vizzarro        metadata=TextParser.find("Allmulticast mode: enabled")
53261d5bc9bSLuca Vizzarro    )
53361d5bc9bSLuca Vizzarro    #: Maximum number of MAC addresses
53461d5bc9bSLuca Vizzarro    max_mac_addresses_num: int = field(
53561d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Maximum number of MAC addresses: (\d+)")
53661d5bc9bSLuca Vizzarro    )
53761d5bc9bSLuca Vizzarro    #: Maximum configurable length of RX packet
53861d5bc9bSLuca Vizzarro    max_hash_mac_addresses_num: int = field(
53961d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Maximum number of MAC addresses of hash filtering: (\d+)")
54061d5bc9bSLuca Vizzarro    )
54161d5bc9bSLuca Vizzarro    #: Minimum size of RX buffer
54261d5bc9bSLuca Vizzarro    min_rx_bufsize: int = field(metadata=TextParser.find_int(r"Minimum size of RX buffer: (\d+)"))
54361d5bc9bSLuca Vizzarro    #: Maximum configurable length of RX packet
54461d5bc9bSLuca Vizzarro    max_rx_packet_length: int = field(
54561d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Maximum configurable length of RX packet: (\d+)")
54661d5bc9bSLuca Vizzarro    )
54761d5bc9bSLuca Vizzarro    #: Maximum configurable size of LRO aggregated packet
54861d5bc9bSLuca Vizzarro    max_lro_packet_size: int = field(
54961d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Maximum configurable size of LRO aggregated packet: (\d+)")
55061d5bc9bSLuca Vizzarro    )
55161d5bc9bSLuca Vizzarro
55261d5bc9bSLuca Vizzarro    #: Current number of RX queues
55361d5bc9bSLuca Vizzarro    rx_queues_num: int = field(metadata=TextParser.find_int(r"Current number of RX queues: (\d+)"))
55461d5bc9bSLuca Vizzarro    #: Max possible RX queues
55561d5bc9bSLuca Vizzarro    max_rx_queues_num: int = field(metadata=TextParser.find_int(r"Max possible RX queues: (\d+)"))
55661d5bc9bSLuca Vizzarro    #: Max possible number of RXDs per queue
55761d5bc9bSLuca Vizzarro    max_queue_rxd_num: int = field(
55861d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Max possible number of RXDs per queue: (\d+)")
55961d5bc9bSLuca Vizzarro    )
56061d5bc9bSLuca Vizzarro    #: Min possible number of RXDs per queue
56161d5bc9bSLuca Vizzarro    min_queue_rxd_num: int = field(
56261d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Min possible number of RXDs per queue: (\d+)")
56361d5bc9bSLuca Vizzarro    )
56461d5bc9bSLuca Vizzarro    #: RXDs number alignment
56561d5bc9bSLuca Vizzarro    rxd_alignment_num: int = field(metadata=TextParser.find_int(r"RXDs number alignment: (\d+)"))
56661d5bc9bSLuca Vizzarro
56761d5bc9bSLuca Vizzarro    #: Current number of TX queues
56861d5bc9bSLuca Vizzarro    tx_queues_num: int = field(metadata=TextParser.find_int(r"Current number of TX queues: (\d+)"))
56961d5bc9bSLuca Vizzarro    #: Max possible TX queues
57061d5bc9bSLuca Vizzarro    max_tx_queues_num: int = field(metadata=TextParser.find_int(r"Max possible TX queues: (\d+)"))
57161d5bc9bSLuca Vizzarro    #: Max possible number of TXDs per queue
57261d5bc9bSLuca Vizzarro    max_queue_txd_num: int = field(
57361d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Max possible number of TXDs per queue: (\d+)")
57461d5bc9bSLuca Vizzarro    )
57561d5bc9bSLuca Vizzarro    #: Min possible number of TXDs per queue
57661d5bc9bSLuca Vizzarro    min_queue_txd_num: int = field(
57761d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Min possible number of TXDs per queue: (\d+)")
57861d5bc9bSLuca Vizzarro    )
57961d5bc9bSLuca Vizzarro    #: TXDs number alignment
58061d5bc9bSLuca Vizzarro    txd_alignment_num: int = field(metadata=TextParser.find_int(r"TXDs number alignment: (\d+)"))
58161d5bc9bSLuca Vizzarro    #: Max segment number per packet
58261d5bc9bSLuca Vizzarro    max_packet_segment_num: int = field(
58361d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Max segment number per packet: (\d+)")
58461d5bc9bSLuca Vizzarro    )
58561d5bc9bSLuca Vizzarro    #: Max segment number per MTU/TSO
58661d5bc9bSLuca Vizzarro    max_mtu_segment_num: int = field(
58761d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"Max segment number per MTU\/TSO: (\d+)")
58861d5bc9bSLuca Vizzarro    )
58961d5bc9bSLuca Vizzarro
59061d5bc9bSLuca Vizzarro    #:
59161d5bc9bSLuca Vizzarro    device_capabilities: DeviceCapabilitiesFlag = field(
59261d5bc9bSLuca Vizzarro        metadata=DeviceCapabilitiesFlag.make_parser(),
59361d5bc9bSLuca Vizzarro    )
59461d5bc9bSLuca Vizzarro    #:
595618d9140SJuraj Linkeš    device_error_handling_mode: DeviceErrorHandlingMode | None = field(
596618d9140SJuraj Linkeš        default=None, metadata=DeviceErrorHandlingMode.make_parser()
59761d5bc9bSLuca Vizzarro    )
59861d5bc9bSLuca Vizzarro    #:
59961d5bc9bSLuca Vizzarro    device_private_info: str | None = field(
60061d5bc9bSLuca Vizzarro        default=None,
60161d5bc9bSLuca Vizzarro        metadata=make_device_private_info_parser(),
60261d5bc9bSLuca Vizzarro    )
60361d5bc9bSLuca Vizzarro
60461d5bc9bSLuca Vizzarro    #:
60561d5bc9bSLuca Vizzarro    hash_key_size: int | None = field(
60661d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find_int(r"Hash key size in bytes: (\d+)")
60761d5bc9bSLuca Vizzarro    )
60861d5bc9bSLuca Vizzarro    #:
60961d5bc9bSLuca Vizzarro    redirection_table_size: int | None = field(
61061d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find_int(r"Redirection table size: (\d+)")
61161d5bc9bSLuca Vizzarro    )
61261d5bc9bSLuca Vizzarro    #:
61361d5bc9bSLuca Vizzarro    supported_rss_offload_flow_types: RSSOffloadTypesFlag = field(
61461d5bc9bSLuca Vizzarro        default=RSSOffloadTypesFlag(0), metadata=RSSOffloadTypesFlag.make_parser()
61561d5bc9bSLuca Vizzarro    )
61661d5bc9bSLuca Vizzarro
61761d5bc9bSLuca Vizzarro    #:
61861d5bc9bSLuca Vizzarro    mac_address: str | None = field(
61961d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find(r"MAC address: ([A-Fa-f0-9:]+)")
62061d5bc9bSLuca Vizzarro    )
62161d5bc9bSLuca Vizzarro    #:
62261d5bc9bSLuca Vizzarro    fw_version: str | None = field(
62361d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find(r"Firmware-version: ([^\r\n]+)")
62461d5bc9bSLuca Vizzarro    )
62561d5bc9bSLuca Vizzarro    #:
62661d5bc9bSLuca Vizzarro    dev_args: str | None = field(default=None, metadata=TextParser.find(r"Devargs: ([^\r\n]+)"))
62761d5bc9bSLuca Vizzarro    #: Socket id of the memory allocation
62861d5bc9bSLuca Vizzarro    mem_alloc_socket_id: int | None = field(
62961d5bc9bSLuca Vizzarro        default=None,
63061d5bc9bSLuca Vizzarro        metadata=TextParser.find_int(r"memory allocation on the socket: (\d+)"),
63161d5bc9bSLuca Vizzarro    )
63261d5bc9bSLuca Vizzarro    #:
63361d5bc9bSLuca Vizzarro    mtu: int | None = field(default=None, metadata=TextParser.find_int(r"MTU: (\d+)"))
63461d5bc9bSLuca Vizzarro
63561d5bc9bSLuca Vizzarro    #:
63661d5bc9bSLuca Vizzarro    vlan_offload: VLANOffloadFlag | None = field(
63761d5bc9bSLuca Vizzarro        default=None,
63861d5bc9bSLuca Vizzarro        metadata=VLANOffloadFlag.make_parser(),
63961d5bc9bSLuca Vizzarro    )
64061d5bc9bSLuca Vizzarro
64161d5bc9bSLuca Vizzarro    #: Maximum size of RX buffer
64261d5bc9bSLuca Vizzarro    max_rx_bufsize: int | None = field(
64361d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find_int(r"Maximum size of RX buffer: (\d+)")
64461d5bc9bSLuca Vizzarro    )
64561d5bc9bSLuca Vizzarro    #: Maximum number of VFs
64661d5bc9bSLuca Vizzarro    max_vfs_num: int | None = field(
64761d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find_int(r"Maximum number of VFs: (\d+)")
64861d5bc9bSLuca Vizzarro    )
64961d5bc9bSLuca Vizzarro    #: Maximum number of VMDq pools
65061d5bc9bSLuca Vizzarro    max_vmdq_pools_num: int | None = field(
65161d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find_int(r"Maximum number of VMDq pools: (\d+)")
65261d5bc9bSLuca Vizzarro    )
65361d5bc9bSLuca Vizzarro
65461d5bc9bSLuca Vizzarro    #:
65561d5bc9bSLuca Vizzarro    switch_name: str | None = field(
65661d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find(r"Switch name: ([\r\n]+)")
65761d5bc9bSLuca Vizzarro    )
65861d5bc9bSLuca Vizzarro    #:
65961d5bc9bSLuca Vizzarro    switch_domain_id: int | None = field(
66061d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find_int(r"Switch domain Id: (\d+)")
66161d5bc9bSLuca Vizzarro    )
66261d5bc9bSLuca Vizzarro    #:
66361d5bc9bSLuca Vizzarro    switch_port_id: int | None = field(
66461d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find_int(r"Switch Port Id: (\d+)")
66561d5bc9bSLuca Vizzarro    )
66661d5bc9bSLuca Vizzarro    #:
66761d5bc9bSLuca Vizzarro    switch_rx_domain: int | None = field(
66861d5bc9bSLuca Vizzarro        default=None, metadata=TextParser.find_int(r"Switch Rx domain: (\d+)")
66961d5bc9bSLuca Vizzarro    )
67061d5bc9bSLuca Vizzarro
67161d5bc9bSLuca Vizzarro
67253eacf3dSLuca Vizzarro@dataclass
67353eacf3dSLuca Vizzarroclass TestPmdPortStats(TextParser):
67453eacf3dSLuca Vizzarro    """Port statistics."""
67553eacf3dSLuca Vizzarro
67653eacf3dSLuca Vizzarro    #:
67753eacf3dSLuca Vizzarro    port_id: int = field(metadata=TextParser.find_int(r"NIC statistics for port (\d+)"))
67853eacf3dSLuca Vizzarro
67953eacf3dSLuca Vizzarro    #:
68053eacf3dSLuca Vizzarro    rx_packets: int = field(metadata=TextParser.find_int(r"RX-packets:\s+(\d+)"))
68153eacf3dSLuca Vizzarro    #:
68253eacf3dSLuca Vizzarro    rx_missed: int = field(metadata=TextParser.find_int(r"RX-missed:\s+(\d+)"))
68353eacf3dSLuca Vizzarro    #:
68453eacf3dSLuca Vizzarro    rx_bytes: int = field(metadata=TextParser.find_int(r"RX-bytes:\s+(\d+)"))
68553eacf3dSLuca Vizzarro    #:
68653eacf3dSLuca Vizzarro    rx_errors: int = field(metadata=TextParser.find_int(r"RX-errors:\s+(\d+)"))
68753eacf3dSLuca Vizzarro    #:
68853eacf3dSLuca Vizzarro    rx_nombuf: int = field(metadata=TextParser.find_int(r"RX-nombuf:\s+(\d+)"))
68953eacf3dSLuca Vizzarro
69053eacf3dSLuca Vizzarro    #:
69153eacf3dSLuca Vizzarro    tx_packets: int = field(metadata=TextParser.find_int(r"TX-packets:\s+(\d+)"))
69253eacf3dSLuca Vizzarro    #:
69353eacf3dSLuca Vizzarro    tx_errors: int = field(metadata=TextParser.find_int(r"TX-errors:\s+(\d+)"))
69453eacf3dSLuca Vizzarro    #:
69553eacf3dSLuca Vizzarro    tx_bytes: int = field(metadata=TextParser.find_int(r"TX-bytes:\s+(\d+)"))
69653eacf3dSLuca Vizzarro
69753eacf3dSLuca Vizzarro    #:
69853eacf3dSLuca Vizzarro    rx_pps: int = field(metadata=TextParser.find_int(r"Rx-pps:\s+(\d+)"))
69953eacf3dSLuca Vizzarro    #:
70053eacf3dSLuca Vizzarro    rx_bps: int = field(metadata=TextParser.find_int(r"Rx-bps:\s+(\d+)"))
70153eacf3dSLuca Vizzarro
70253eacf3dSLuca Vizzarro    #:
70353eacf3dSLuca Vizzarro    tx_pps: int = field(metadata=TextParser.find_int(r"Tx-pps:\s+(\d+)"))
70453eacf3dSLuca Vizzarro    #:
70553eacf3dSLuca Vizzarro    tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
70653eacf3dSLuca Vizzarro
70753eacf3dSLuca Vizzarro
708a91d5f47SJeremy Spewockclass PacketOffloadFlag(Flag):
709a91d5f47SJeremy Spewock    """Flag representing the Packet Offload Features Flags in DPDK.
710a91d5f47SJeremy Spewock
711a91d5f47SJeremy Spewock    Values in this class are taken from the definitions in the RTE MBUF core library in DPDK
712a91d5f47SJeremy Spewock    located in ``lib/mbuf/rte_mbuf_core.h``. It is expected that flag values in this class will
713a91d5f47SJeremy Spewock    match the values they are set to in said DPDK library with one exception; all values must be
714a91d5f47SJeremy Spewock    unique. For example, the definitions for unknown checksum flags in ``rte_mbuf_core.h`` are all
715a91d5f47SJeremy Spewock    set to :data:`0`, but it is valuable to distinguish between them in this framework. For this
716a91d5f47SJeremy Spewock    reason flags that are not unique in the DPDK library are set either to values within the
717a91d5f47SJeremy Spewock    RTE_MBUF_F_FIRST_FREE-RTE_MBUF_F_LAST_FREE range for Rx or shifted 61+ bits for Tx.
718a91d5f47SJeremy Spewock
719a91d5f47SJeremy Spewock    References:
720a91d5f47SJeremy Spewock        DPDK lib: ``lib/mbuf/rte_mbuf_core.h``
721a91d5f47SJeremy Spewock    """
722a91d5f47SJeremy Spewock
723a91d5f47SJeremy Spewock    # RX flags
724a91d5f47SJeremy Spewock
725a91d5f47SJeremy Spewock    #: The RX packet is a 802.1q VLAN packet, and the tci has been saved in mbuf->vlan_tci. If the
726a91d5f47SJeremy Spewock    #: flag RTE_MBUF_F_RX_VLAN_STRIPPED is also present, the VLAN header has been stripped from
727a91d5f47SJeremy Spewock    #: mbuf data, else it is still present.
728a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_VLAN = auto()
729a91d5f47SJeremy Spewock
730a91d5f47SJeremy Spewock    #: RX packet with RSS hash result.
731a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_RSS_HASH = auto()
732a91d5f47SJeremy Spewock
733a91d5f47SJeremy Spewock    #: RX packet with FDIR match indicate.
734a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_FDIR = auto()
735a91d5f47SJeremy Spewock
736a91d5f47SJeremy Spewock    #: This flag is set when the outermost IP header checksum is detected as wrong by the hardware.
737a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD = 1 << 5
738a91d5f47SJeremy Spewock
739a91d5f47SJeremy Spewock    #: A vlan has been stripped by the hardware and its tci is saved in mbuf->vlan_tci. This can
740a91d5f47SJeremy Spewock    #: only happen if vlan stripping is enabled in the RX configuration of the PMD. When
741a91d5f47SJeremy Spewock    #: RTE_MBUF_F_RX_VLAN_STRIPPED is set, RTE_MBUF_F_RX_VLAN must also be set.
742a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_VLAN_STRIPPED = auto()
743a91d5f47SJeremy Spewock
744a91d5f47SJeremy Spewock    #: No information about the RX IP checksum. Value is 0 in the DPDK library.
745a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = 1 << 23
746a91d5f47SJeremy Spewock    #: The IP checksum in the packet is wrong.
747a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_IP_CKSUM_BAD = 1 << 4
748a91d5f47SJeremy Spewock    #: The IP checksum in the packet is valid.
749a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_IP_CKSUM_GOOD = 1 << 7
750a91d5f47SJeremy Spewock    #: The IP checksum is not correct in the packet data, but the integrity of the IP header is
751a91d5f47SJeremy Spewock    #: verified. Value is RTE_MBUF_F_RX_IP_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_GOOD in the DPDK
752a91d5f47SJeremy Spewock    #: library.
753a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_IP_CKSUM_NONE = 1 << 24
754a91d5f47SJeremy Spewock
755a91d5f47SJeremy Spewock    #: No information about the RX L4 checksum. Value is 0 in the DPDK library.
756a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = 1 << 25
757a91d5f47SJeremy Spewock    #: The L4 checksum in the packet is wrong.
758a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_L4_CKSUM_BAD = 1 << 3
759a91d5f47SJeremy Spewock    #: The L4 checksum in the packet is valid.
760a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_L4_CKSUM_GOOD = 1 << 8
761a91d5f47SJeremy Spewock    #: The L4 checksum is not correct in the packet data, but the integrity of the L4 data is
762a91d5f47SJeremy Spewock    #: verified. Value is RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD in the DPDK
763a91d5f47SJeremy Spewock    #: library.
764a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_L4_CKSUM_NONE = 1 << 26
765a91d5f47SJeremy Spewock
766a91d5f47SJeremy Spewock    #: RX IEEE1588 L2 Ethernet PT Packet.
767a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_IEEE1588_PTP = 1 << 9
768a91d5f47SJeremy Spewock    #: RX IEEE1588 L2/L4 timestamped packet.
769a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_IEEE1588_TMST = 1 << 10
770a91d5f47SJeremy Spewock
771a91d5f47SJeremy Spewock    #: FD id reported if FDIR match.
772a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_FDIR_ID = 1 << 13
773a91d5f47SJeremy Spewock    #: Flexible bytes reported if FDIR match.
774a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_FDIR_FLX = 1 << 14
775a91d5f47SJeremy Spewock
776a91d5f47SJeremy Spewock    #: If both RTE_MBUF_F_RX_QINQ_STRIPPED and RTE_MBUF_F_RX_VLAN_STRIPPED are set, the 2 VLANs
777a91d5f47SJeremy Spewock    #: have been stripped by the hardware. If RTE_MBUF_F_RX_QINQ_STRIPPED is set and
778a91d5f47SJeremy Spewock    #: RTE_MBUF_F_RX_VLAN_STRIPPED is unset, only the outer VLAN is removed from packet data.
779a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_QINQ_STRIPPED = auto()
780a91d5f47SJeremy Spewock
781a91d5f47SJeremy Spewock    #: When packets are coalesced by a hardware or virtual driver, this flag can be set in the RX
782a91d5f47SJeremy Spewock    #: mbuf, meaning that the m->tso_segsz field is valid and is set to the segment size of
783a91d5f47SJeremy Spewock    #: original packets.
784a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_LRO = auto()
785a91d5f47SJeremy Spewock
786a91d5f47SJeremy Spewock    #: Indicate that security offload processing was applied on the RX packet.
787a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_SEC_OFFLOAD = 1 << 18
788a91d5f47SJeremy Spewock    #: Indicate that security offload processing failed on the RX packet.
789a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED = auto()
790a91d5f47SJeremy Spewock
791a91d5f47SJeremy Spewock    #: The RX packet is a double VLAN. If this flag is set, RTE_MBUF_F_RX_VLAN must also be set. If
792a91d5f47SJeremy Spewock    #: the flag RTE_MBUF_F_RX_QINQ_STRIPPED is also present, both VLANs headers have been stripped
793a91d5f47SJeremy Spewock    #: from mbuf data, else they are still present.
794a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_QINQ = auto()
795a91d5f47SJeremy Spewock
796a91d5f47SJeremy Spewock    #: No info about the outer RX L4 checksum. Value is 0 in the DPDK library.
797a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = 1 << 27
798a91d5f47SJeremy Spewock    #: The outer L4 checksum in the packet is wrong
799a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = 1 << 21
800a91d5f47SJeremy Spewock    #: The outer L4 checksum in the packet is valid
801a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = 1 << 22
802a91d5f47SJeremy Spewock    #: Invalid outer L4 checksum state. Value is
803a91d5f47SJeremy Spewock    #: RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD | RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD in the DPDK library.
804a91d5f47SJeremy Spewock    RTE_MBUF_F_RX_OUTER_L4_CKSUM_INVALID = 1 << 28
805a91d5f47SJeremy Spewock
806a91d5f47SJeremy Spewock    # TX flags
807a91d5f47SJeremy Spewock
808a91d5f47SJeremy Spewock    #: Outer UDP checksum offload flag. This flag is used for enabling outer UDP checksum in PMD.
809a91d5f47SJeremy Spewock    #: To use outer UDP checksum, the user either needs to enable the following in mbuf:
810a91d5f47SJeremy Spewock    #:
811a91d5f47SJeremy Spewock    #:  a) Fill outer_l2_len and outer_l3_len in mbuf.
812a91d5f47SJeremy Spewock    #:  b) Set the RTE_MBUF_F_TX_OUTER_UDP_CKSUM flag.
813a91d5f47SJeremy Spewock    #:  c) Set the RTE_MBUF_F_TX_OUTER_IPV4 or RTE_MBUF_F_TX_OUTER_IPV6 flag.
814a91d5f47SJeremy Spewock    #:
815a91d5f47SJeremy Spewock    #: Or configure RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM offload flag.
816a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_OUTER_UDP_CKSUM = 1 << 41
817a91d5f47SJeremy Spewock
818a91d5f47SJeremy Spewock    #: UDP Fragmentation Offload flag. This flag is used for enabling UDP fragmentation in SW or in
819a91d5f47SJeremy Spewock    #: HW.
820a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_UDP_SEG = auto()
821a91d5f47SJeremy Spewock
822a91d5f47SJeremy Spewock    #: Request security offload processing on the TX packet. To use Tx security offload, the user
823a91d5f47SJeremy Spewock    #: needs to fill l2_len in mbuf indicating L2 header size and where L3 header starts.
824a91d5f47SJeremy Spewock    #: Similarly, l3_len should also be filled along with ol_flags reflecting current L3 type.
825a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_SEC_OFFLOAD = auto()
826a91d5f47SJeremy Spewock
827a91d5f47SJeremy Spewock    #: Offload the MACsec. This flag must be set by the application to enable this offload feature
828a91d5f47SJeremy Spewock    #: for a packet to be transmitted.
829a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_MACSEC = auto()
830a91d5f47SJeremy Spewock
831a91d5f47SJeremy Spewock    # Bits 45:48 are used for the tunnel type in ``lib/mbuf/rte_mbuf_core.h``, but some are modified
832a91d5f47SJeremy Spewock    # in this Flag to maintain uniqueness. The tunnel type must be specified for TSO or checksum on
833a91d5f47SJeremy Spewock    # the inner part of tunnel packets. These flags can be used with RTE_MBUF_F_TX_TCP_SEG for TSO,
834a91d5f47SJeremy Spewock    # or RTE_MBUF_F_TX_xxx_CKSUM. The mbuf fields for inner and outer header lengths are required:
835a91d5f47SJeremy Spewock    # outer_l2_len, outer_l3_len, l2_len, l3_len, l4_len and tso_segsz for TSO.
836a91d5f47SJeremy Spewock
837a91d5f47SJeremy Spewock    #:
838a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_VXLAN = 1 << 45
839a91d5f47SJeremy Spewock    #:
840a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_GRE = 1 << 46
841a91d5f47SJeremy Spewock    #: Value is 3 << 45 in the DPDK library.
842a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_IPIP = 1 << 61
843a91d5f47SJeremy Spewock    #:
844a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_GENEVE = 1 << 47
845a91d5f47SJeremy Spewock    #: TX packet with MPLS-in-UDP RFC 7510 header. Value is 5 << 45 in the DPDK library.
846a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_MPLSINUDP = 1 << 62
847a91d5f47SJeremy Spewock    #: Value is 6 << 45 in the DPDK library.
848a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE = 1 << 63
849a91d5f47SJeremy Spewock    #: Value is 7 << 45 in the DPDK library.
850a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_GTP = 1 << 64
851a91d5f47SJeremy Spewock    #:
852a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_ESP = 1 << 48
853a91d5f47SJeremy Spewock    #: Generic IP encapsulated tunnel type, used for TSO and checksum offload. This can be used for
854a91d5f47SJeremy Spewock    #: tunnels which are not standards or listed above. It is preferred to use specific tunnel
855a91d5f47SJeremy Spewock    #: flags like RTE_MBUF_F_TX_TUNNEL_GRE or RTE_MBUF_F_TX_TUNNEL_IPIP if possible. The ethdev
856a91d5f47SJeremy Spewock    #: must be configured with RTE_ETH_TX_OFFLOAD_IP_TNL_TSO.  Outer and inner checksums are done
857a91d5f47SJeremy Spewock    #: according to the existing flags like RTE_MBUF_F_TX_xxx_CKSUM. Specific tunnel headers that
858a91d5f47SJeremy Spewock    #: contain payload length, sequence id or checksum are not expected to be updated. Value is
859a91d5f47SJeremy Spewock    #: 0xD << 45 in the DPDK library.
860a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_IP = 1 << 65
861a91d5f47SJeremy Spewock    #: Generic UDP encapsulated tunnel type, used for TSO and checksum offload. UDP tunnel type
862a91d5f47SJeremy Spewock    #: implies outer IP layer. It can be used for tunnels which are not standards or listed above.
863a91d5f47SJeremy Spewock    #: It is preferred to use specific tunnel flags like RTE_MBUF_F_TX_TUNNEL_VXLAN if possible.
864a91d5f47SJeremy Spewock    #: The ethdev must be configured with RTE_ETH_TX_OFFLOAD_UDP_TNL_TSO. Outer and inner checksums
865a91d5f47SJeremy Spewock    #: are done according to the existing flags like RTE_MBUF_F_TX_xxx_CKSUM. Specific tunnel
866a91d5f47SJeremy Spewock    #: headers that contain payload length, sequence id or checksum are not expected to be updated.
867a91d5f47SJeremy Spewock    #: value is 0xE << 45 in the DPDK library.
868a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TUNNEL_UDP = 1 << 66
869a91d5f47SJeremy Spewock
870a91d5f47SJeremy Spewock    #: Double VLAN insertion (QinQ) request to driver, driver may offload the insertion based on
871a91d5f47SJeremy Spewock    #: device capability. Mbuf 'vlan_tci' & 'vlan_tci_outer' must be valid when this flag is set.
872a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_QINQ = 1 << 49
873a91d5f47SJeremy Spewock
874a91d5f47SJeremy Spewock    #: TCP segmentation offload. To enable this offload feature for a packet to be transmitted on
875a91d5f47SJeremy Spewock    #: hardware supporting TSO:
876a91d5f47SJeremy Spewock    #:
877a91d5f47SJeremy Spewock    #:  - set the RTE_MBUF_F_TX_TCP_SEG flag in mbuf->ol_flags (this flag implies
878a91d5f47SJeremy Spewock    #:      RTE_MBUF_F_TX_TCP_CKSUM)
879a91d5f47SJeremy Spewock    #:  - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6
880a91d5f47SJeremy Spewock    #:      * if it's IPv4, set the RTE_MBUF_F_TX_IP_CKSUM flag
881a91d5f47SJeremy Spewock    #:  - fill the mbuf offload information: l2_len, l3_len, l4_len, tso_segsz
882a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TCP_SEG = auto()
883a91d5f47SJeremy Spewock
884a91d5f47SJeremy Spewock    #: TX IEEE1588 packet to timestamp.
885a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_IEEE1588_TMST = auto()
886a91d5f47SJeremy Spewock
887a91d5f47SJeremy Spewock    # Bits 52+53 used for L4 packet type with checksum enabled in ``lib/mbuf/rte_mbuf_core.h`` but
888a91d5f47SJeremy Spewock    # some values must be modified in this framework to maintain uniqueness. To use hardware
889a91d5f47SJeremy Spewock    # L4 checksum offload, the user needs to:
890a91d5f47SJeremy Spewock    #
891a91d5f47SJeremy Spewock    # - fill l2_len and l3_len in mbuf
892a91d5f47SJeremy Spewock    # - set the flags RTE_MBUF_F_TX_TCP_CKSUM, RTE_MBUF_F_TX_SCTP_CKSUM or
893a91d5f47SJeremy Spewock    #   RTE_MBUF_F_TX_UDP_CKSUM
894a91d5f47SJeremy Spewock    # - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6
895a91d5f47SJeremy Spewock
896a91d5f47SJeremy Spewock    #: Disable L4 cksum of TX pkt. Value is 0 in the DPDK library.
897a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_L4_NO_CKSUM = 1 << 67
898a91d5f47SJeremy Spewock    #: TCP cksum of TX pkt. Computed by NIC.
899a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_TCP_CKSUM = 1 << 52
900a91d5f47SJeremy Spewock    #: SCTP cksum of TX pkt. Computed by NIC.
901a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_SCTP_CKSUM = 1 << 53
902a91d5f47SJeremy Spewock    #: UDP cksum of TX pkt. Computed by NIC. Value is 3 << 52 in the DPDK library.
903a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_UDP_CKSUM = 1 << 68
904a91d5f47SJeremy Spewock
905a91d5f47SJeremy Spewock    #: Offload the IP checksum in the hardware. The flag RTE_MBUF_F_TX_IPV4 should also be set by
906a91d5f47SJeremy Spewock    #: the application, although a PMD will only check RTE_MBUF_F_TX_IP_CKSUM.
907a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_IP_CKSUM = 1 << 54
908a91d5f47SJeremy Spewock
909a91d5f47SJeremy Spewock    #: Packet is IPv4. This flag must be set when using any offload feature (TSO, L3 or L4
910a91d5f47SJeremy Spewock    #: checksum) to tell the NIC that the packet is an IPv4 packet. If the packet is a tunneled
911a91d5f47SJeremy Spewock    #: packet, this flag is related to the inner headers.
912a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_IPV4 = auto()
913a91d5f47SJeremy Spewock    #: Packet is IPv6. This flag must be set when using an offload feature (TSO or L4 checksum) to
914a91d5f47SJeremy Spewock    #: tell the NIC that the packet is an IPv6 packet. If the packet is a tunneled packet, this
915a91d5f47SJeremy Spewock    #: flag is related to the inner headers.
916a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_IPV6 = auto()
917a91d5f47SJeremy Spewock    #: VLAN tag insertion request to driver, driver may offload the insertion based on the device
918a91d5f47SJeremy Spewock    #: capability. mbuf 'vlan_tci' field must be valid when this flag is set.
919a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_VLAN = auto()
920a91d5f47SJeremy Spewock
921a91d5f47SJeremy Spewock    #: Offload the IP checksum of an external header in the hardware. The flag
922a91d5f47SJeremy Spewock    #: RTE_MBUF_F_TX_OUTER_IPV4 should also be set by the application, although a PMD will only
923a91d5f47SJeremy Spewock    #: check RTE_MBUF_F_TX_OUTER_IP_CKSUM.
924a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_OUTER_IP_CKSUM = auto()
925a91d5f47SJeremy Spewock    #: Packet outer header is IPv4. This flag must be set when using any outer offload feature (L3
926a91d5f47SJeremy Spewock    #: or L4 checksum) to tell the NIC that the outer header of the tunneled packet is an IPv4
927a91d5f47SJeremy Spewock    #: packet.
928a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_OUTER_IPV4 = auto()
929a91d5f47SJeremy Spewock    #: Packet outer header is IPv6. This flag must be set when using any outer offload feature (L4
930a91d5f47SJeremy Spewock    #: checksum) to tell the NIC that the outer header of the tunneled packet is an IPv6 packet.
931a91d5f47SJeremy Spewock    RTE_MBUF_F_TX_OUTER_IPV6 = auto()
932a91d5f47SJeremy Spewock
933a91d5f47SJeremy Spewock    @classmethod
934a91d5f47SJeremy Spewock    def from_list_string(cls, names: str) -> Self:
935a91d5f47SJeremy Spewock        """Makes a flag from a whitespace-separated list of names.
936a91d5f47SJeremy Spewock
937a91d5f47SJeremy Spewock        Args:
938a91d5f47SJeremy Spewock            names: a whitespace-separated list containing the members of this flag.
939a91d5f47SJeremy Spewock
940a91d5f47SJeremy Spewock        Returns:
941a91d5f47SJeremy Spewock            An instance of this flag.
942a91d5f47SJeremy Spewock        """
943a91d5f47SJeremy Spewock        flag = cls(0)
944a91d5f47SJeremy Spewock        for name in names.split():
945a91d5f47SJeremy Spewock            flag |= cls.from_str(name)
946a91d5f47SJeremy Spewock        return flag
947a91d5f47SJeremy Spewock
948a91d5f47SJeremy Spewock    @classmethod
949a91d5f47SJeremy Spewock    def from_str(cls, name: str) -> Self:
950a91d5f47SJeremy Spewock        """Makes a flag matching the supplied name.
951a91d5f47SJeremy Spewock
952a91d5f47SJeremy Spewock        Args:
953a91d5f47SJeremy Spewock            name: a valid member of this flag in text
954a91d5f47SJeremy Spewock        Returns:
955a91d5f47SJeremy Spewock            An instance of this flag.
956a91d5f47SJeremy Spewock        """
957a91d5f47SJeremy Spewock        member_name = name.strip().replace("-", "_")
958a91d5f47SJeremy Spewock        return cls[member_name]
959a91d5f47SJeremy Spewock
960a91d5f47SJeremy Spewock    @classmethod
961a91d5f47SJeremy Spewock    def make_parser(cls) -> ParserFn:
962a91d5f47SJeremy Spewock        """Makes a parser function.
963a91d5f47SJeremy Spewock
964a91d5f47SJeremy Spewock        Returns:
965a91d5f47SJeremy Spewock            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a
966a91d5f47SJeremy Spewock                parser function that makes an instance of this flag from text.
967a91d5f47SJeremy Spewock        """
968a91d5f47SJeremy Spewock        return TextParser.wrap(
969a91d5f47SJeremy Spewock            TextParser.find(r"ol_flags: ([^\n]+)"),
970a91d5f47SJeremy Spewock            cls.from_list_string,
971a91d5f47SJeremy Spewock        )
972a91d5f47SJeremy Spewock
973a91d5f47SJeremy Spewock
974a91d5f47SJeremy Spewockclass RtePTypes(Flag):
975a91d5f47SJeremy Spewock    """Flag representing possible packet types in DPDK verbose output.
976a91d5f47SJeremy Spewock
977a91d5f47SJeremy Spewock    Values in this class are derived from definitions in the RTE MBUF ptype library in DPDK located
978a91d5f47SJeremy Spewock    in ``lib/mbuf/rte_mbuf_ptype.h``. Specifically, the names of values in this class should match
979a91d5f47SJeremy Spewock    the possible return options from the functions ``rte_get_ptype_*_name`` in ``rte_mbuf_ptype.c``.
980a91d5f47SJeremy Spewock
981a91d5f47SJeremy Spewock    References:
982a91d5f47SJeremy Spewock        DPDK lib: ``lib/mbuf/rte_mbuf_ptype.h``
983a91d5f47SJeremy Spewock        DPDK ptype name formatting functions: ``lib/mbuf/rte_mbuf_ptype.c:rte_get_ptype_*_name()``
984a91d5f47SJeremy Spewock    """
985a91d5f47SJeremy Spewock
986a91d5f47SJeremy Spewock    # L2
987a91d5f47SJeremy Spewock    #: Ethernet packet type. This is used for outer packet for tunneling cases.
988a91d5f47SJeremy Spewock    L2_ETHER = auto()
989a91d5f47SJeremy Spewock    #: Ethernet packet type for time sync.
990a91d5f47SJeremy Spewock    L2_ETHER_TIMESYNC = auto()
991a91d5f47SJeremy Spewock    #: ARP (Address Resolution Protocol) packet type.
992a91d5f47SJeremy Spewock    L2_ETHER_ARP = auto()
993a91d5f47SJeremy Spewock    #: LLDP (Link Layer Discovery Protocol) packet type.
994a91d5f47SJeremy Spewock    L2_ETHER_LLDP = auto()
995a91d5f47SJeremy Spewock    #: NSH (Network Service Header) packet type.
996a91d5f47SJeremy Spewock    L2_ETHER_NSH = auto()
997a91d5f47SJeremy Spewock    #: VLAN packet type.
998a91d5f47SJeremy Spewock    L2_ETHER_VLAN = auto()
999a91d5f47SJeremy Spewock    #: QinQ packet type.
1000a91d5f47SJeremy Spewock    L2_ETHER_QINQ = auto()
1001a91d5f47SJeremy Spewock    #: PPPOE packet type.
1002a91d5f47SJeremy Spewock    L2_ETHER_PPPOE = auto()
1003a91d5f47SJeremy Spewock    #: FCoE packet type..
1004a91d5f47SJeremy Spewock    L2_ETHER_FCOE = auto()
1005a91d5f47SJeremy Spewock    #: MPLS packet type.
1006a91d5f47SJeremy Spewock    L2_ETHER_MPLS = auto()
1007a91d5f47SJeremy Spewock    #: No L2 packet information.
1008a91d5f47SJeremy Spewock    L2_UNKNOWN = auto()
1009a91d5f47SJeremy Spewock
1010a91d5f47SJeremy Spewock    # L3
1011a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 4 packet type. This is used for outer packet for tunneling
1012a91d5f47SJeremy Spewock    #: cases, and does not contain any header option.
1013a91d5f47SJeremy Spewock    L3_IPV4 = auto()
1014a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 4 packet type. This is used for outer packet for tunneling
1015a91d5f47SJeremy Spewock    #: cases, and contains header options.
1016a91d5f47SJeremy Spewock    L3_IPV4_EXT = auto()
1017a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 6 packet type. This is used for outer packet for tunneling
1018a91d5f47SJeremy Spewock    #: cases, and does not contain any extension header.
1019a91d5f47SJeremy Spewock    L3_IPV6 = auto()
1020a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 4 packet type. This is used for outer packet for tunneling
1021a91d5f47SJeremy Spewock    #: cases, and may or maynot contain header options.
1022a91d5f47SJeremy Spewock    L3_IPV4_EXT_UNKNOWN = auto()
1023a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 6 packet type. This is used for outer packet for tunneling
1024a91d5f47SJeremy Spewock    #: cases, and contains extension headers.
1025a91d5f47SJeremy Spewock    L3_IPV6_EXT = auto()
1026a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 6 packet type. This is used for outer packet for tunneling
1027a91d5f47SJeremy Spewock    #: cases, and may or maynot contain extension headers.
1028a91d5f47SJeremy Spewock    L3_IPV6_EXT_UNKNOWN = auto()
1029a91d5f47SJeremy Spewock    #: No L3 packet information.
1030a91d5f47SJeremy Spewock    L3_UNKNOWN = auto()
1031a91d5f47SJeremy Spewock
1032a91d5f47SJeremy Spewock    # L4
1033a91d5f47SJeremy Spewock    #: TCP (Transmission Control Protocol) packet type. This is used for outer packet for tunneling
1034a91d5f47SJeremy Spewock    #: cases.
1035a91d5f47SJeremy Spewock    L4_TCP = auto()
1036a91d5f47SJeremy Spewock    #: UDP (User Datagram Protocol) packet type. This is used for outer packet for tunneling cases.
1037a91d5f47SJeremy Spewock    L4_UDP = auto()
1038a91d5f47SJeremy Spewock    #: Fragmented IP (Internet Protocol) packet type. This is used for outer packet for tunneling
1039a91d5f47SJeremy Spewock    #: cases and refers to those packets of any IP types which can be recognized as fragmented. A
1040a91d5f47SJeremy Spewock    #: fragmented packet cannot be recognized as any other L4 types (RTE_PTYPE_L4_TCP,
1041a91d5f47SJeremy Spewock    #: RTE_PTYPE_L4_UDP, RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP, RTE_PTYPE_L4_NONFRAG).
1042a91d5f47SJeremy Spewock    L4_FRAG = auto()
1043a91d5f47SJeremy Spewock    #: SCTP (Stream Control Transmission Protocol) packet type. This is used for outer packet for
1044a91d5f47SJeremy Spewock    #: tunneling cases.
1045a91d5f47SJeremy Spewock    L4_SCTP = auto()
1046a91d5f47SJeremy Spewock    #: ICMP (Internet Control Message Protocol) packet type. This is used for outer packet for
1047a91d5f47SJeremy Spewock    #: tunneling cases.
1048a91d5f47SJeremy Spewock    L4_ICMP = auto()
1049a91d5f47SJeremy Spewock    #: Non-fragmented IP (Internet Protocol) packet type. This is used for outer packet for
1050a91d5f47SJeremy Spewock    #: tunneling cases and refers to those packets of any IP types, that cannot be recognized as
1051a91d5f47SJeremy Spewock    #: any of the above L4 types (RTE_PTYPE_L4_TCP, RTE_PTYPE_L4_UDP, RTE_PTYPE_L4_FRAG,
1052a91d5f47SJeremy Spewock    #: RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP).
1053a91d5f47SJeremy Spewock    L4_NONFRAG = auto()
1054a91d5f47SJeremy Spewock    #: IGMP (Internet Group Management Protocol) packet type.
1055a91d5f47SJeremy Spewock    L4_IGMP = auto()
1056a91d5f47SJeremy Spewock    #: No L4 packet information.
1057a91d5f47SJeremy Spewock    L4_UNKNOWN = auto()
1058a91d5f47SJeremy Spewock
1059a91d5f47SJeremy Spewock    # Tunnel
1060a91d5f47SJeremy Spewock    #: IP (Internet Protocol) in IP (Internet Protocol) tunneling packet type.
1061a91d5f47SJeremy Spewock    TUNNEL_IP = auto()
1062a91d5f47SJeremy Spewock    #: GRE (Generic Routing Encapsulation) tunneling packet type.
1063a91d5f47SJeremy Spewock    TUNNEL_GRE = auto()
1064a91d5f47SJeremy Spewock    #: VXLAN (Virtual eXtensible Local Area Network) tunneling packet type.
1065a91d5f47SJeremy Spewock    TUNNEL_VXLAN = auto()
1066a91d5f47SJeremy Spewock    #: NVGRE (Network Virtualization using Generic Routing Encapsulation) tunneling packet type.
1067a91d5f47SJeremy Spewock    TUNNEL_NVGRE = auto()
1068a91d5f47SJeremy Spewock    #: GENEVE (Generic Network Virtualization Encapsulation) tunneling packet type.
1069a91d5f47SJeremy Spewock    TUNNEL_GENEVE = auto()
1070a91d5f47SJeremy Spewock    #: Tunneling packet type of Teredo, VXLAN (Virtual eXtensible Local Area Network) or GRE
1071a91d5f47SJeremy Spewock    #: (Generic Routing Encapsulation) could be recognized as this packet type, if they can not be
1072a91d5f47SJeremy Spewock    #: recognized independently as of hardware capability.
1073a91d5f47SJeremy Spewock    TUNNEL_GRENAT = auto()
1074a91d5f47SJeremy Spewock    #: GTP-C (GPRS Tunnelling Protocol) control tunneling packet type.
1075a91d5f47SJeremy Spewock    TUNNEL_GTPC = auto()
1076a91d5f47SJeremy Spewock    #: GTP-U (GPRS Tunnelling Protocol) user data tunneling packet type.
1077a91d5f47SJeremy Spewock    TUNNEL_GTPU = auto()
1078a91d5f47SJeremy Spewock    #: ESP (IP Encapsulating Security Payload) tunneling packet type.
1079a91d5f47SJeremy Spewock    TUNNEL_ESP = auto()
1080a91d5f47SJeremy Spewock    #: L2TP (Layer 2 Tunneling Protocol) tunneling packet type.
1081a91d5f47SJeremy Spewock    TUNNEL_L2TP = auto()
1082a91d5f47SJeremy Spewock    #: VXLAN-GPE (VXLAN Generic Protocol Extension) tunneling packet type.
1083a91d5f47SJeremy Spewock    TUNNEL_VXLAN_GPE = auto()
1084a91d5f47SJeremy Spewock    #: MPLS-in-UDP tunneling packet type (RFC 7510).
1085a91d5f47SJeremy Spewock    TUNNEL_MPLS_IN_UDP = auto()
1086a91d5f47SJeremy Spewock    #: MPLS-in-GRE tunneling packet type (RFC 4023).
1087a91d5f47SJeremy Spewock    TUNNEL_MPLS_IN_GRE = auto()
1088a91d5f47SJeremy Spewock    #: No tunnel information found on the packet.
1089a91d5f47SJeremy Spewock    TUNNEL_UNKNOWN = auto()
1090a91d5f47SJeremy Spewock
1091a91d5f47SJeremy Spewock    # Inner L2
1092a91d5f47SJeremy Spewock    #: Ethernet packet type. This is used for inner packet type only.
1093a91d5f47SJeremy Spewock    INNER_L2_ETHER = auto()
1094a91d5f47SJeremy Spewock    #: Ethernet packet type with VLAN (Virtual Local Area Network) tag.
1095a91d5f47SJeremy Spewock    INNER_L2_ETHER_VLAN = auto()
1096a91d5f47SJeremy Spewock    #: QinQ packet type.
1097a91d5f47SJeremy Spewock    INNER_L2_ETHER_QINQ = auto()
1098a91d5f47SJeremy Spewock    #: No inner L2 information found on the packet.
1099a91d5f47SJeremy Spewock    INNER_L2_UNKNOWN = auto()
1100a91d5f47SJeremy Spewock
1101a91d5f47SJeremy Spewock    # Inner L3
1102a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 4 packet type. This is used for inner packet only, and does
1103a91d5f47SJeremy Spewock    #: not contain any header option.
1104a91d5f47SJeremy Spewock    INNER_L3_IPV4 = auto()
1105a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 4 packet type. This is used for inner packet only, and
1106a91d5f47SJeremy Spewock    #: contains header options.
1107a91d5f47SJeremy Spewock    INNER_L3_IPV4_EXT = auto()
1108a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 6 packet type. This is used for inner packet only, and does
1109a91d5f47SJeremy Spewock    #: not contain any extension header.
1110a91d5f47SJeremy Spewock    INNER_L3_IPV6 = auto()
1111a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 4 packet type. This is used for inner packet only, and may or
1112a91d5f47SJeremy Spewock    #: may not contain header options.
1113a91d5f47SJeremy Spewock    INNER_L3_IPV4_EXT_UNKNOWN = auto()
1114a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 6 packet type. This is used for inner packet only, and
1115a91d5f47SJeremy Spewock    #: contains extension headers.
1116a91d5f47SJeremy Spewock    INNER_L3_IPV6_EXT = auto()
1117a91d5f47SJeremy Spewock    #: IP (Internet Protocol) version 6 packet type. This is used for inner packet only, and may or
1118a91d5f47SJeremy Spewock    #: may not contain extension headers.
1119a91d5f47SJeremy Spewock    INNER_L3_IPV6_EXT_UNKNOWN = auto()
1120a91d5f47SJeremy Spewock    #: No inner L3 information found on the packet.
1121a91d5f47SJeremy Spewock    INNER_L3_UNKNOWN = auto()
1122a91d5f47SJeremy Spewock
1123a91d5f47SJeremy Spewock    # Inner L4
1124a91d5f47SJeremy Spewock    #: TCP (Transmission Control Protocol) packet type. This is used for inner packet only.
1125a91d5f47SJeremy Spewock    INNER_L4_TCP = auto()
1126a91d5f47SJeremy Spewock    #: UDP (User Datagram Protocol) packet type. This is used for inner packet only.
1127a91d5f47SJeremy Spewock    INNER_L4_UDP = auto()
1128a91d5f47SJeremy Spewock    #: Fragmented IP (Internet Protocol) packet type. This is used for inner packet only, and may
1129a91d5f47SJeremy Spewock    #: or maynot have a layer 4 packet.
1130a91d5f47SJeremy Spewock    INNER_L4_FRAG = auto()
1131a91d5f47SJeremy Spewock    #: SCTP (Stream Control Transmission Protocol) packet type. This is used for inner packet only.
1132a91d5f47SJeremy Spewock    INNER_L4_SCTP = auto()
1133a91d5f47SJeremy Spewock    #: ICMP (Internet Control Message Protocol) packet type. This is used for inner packet only.
1134a91d5f47SJeremy Spewock    INNER_L4_ICMP = auto()
1135a91d5f47SJeremy Spewock    #: Non-fragmented IP (Internet Protocol) packet type. It is used for inner packet only, and may
1136a91d5f47SJeremy Spewock    #: or may not have other unknown layer 4 packet types.
1137a91d5f47SJeremy Spewock    INNER_L4_NONFRAG = auto()
1138a91d5f47SJeremy Spewock    #: No inner L4 information found on the packet.
1139a91d5f47SJeremy Spewock    INNER_L4_UNKNOWN = auto()
1140a91d5f47SJeremy Spewock
1141a91d5f47SJeremy Spewock    @classmethod
1142a91d5f47SJeremy Spewock    def from_list_string(cls, names: str) -> Self:
1143a91d5f47SJeremy Spewock        """Makes a flag from a whitespace-separated list of names.
1144a91d5f47SJeremy Spewock
1145a91d5f47SJeremy Spewock        Args:
1146a91d5f47SJeremy Spewock            names: a whitespace-separated list containing the members of this flag.
1147a91d5f47SJeremy Spewock
1148a91d5f47SJeremy Spewock        Returns:
1149a91d5f47SJeremy Spewock            An instance of this flag.
1150a91d5f47SJeremy Spewock        """
1151a91d5f47SJeremy Spewock        flag = cls(0)
1152a91d5f47SJeremy Spewock        for name in names.split():
1153a91d5f47SJeremy Spewock            flag |= cls.from_str(name)
1154a91d5f47SJeremy Spewock        return flag
1155a91d5f47SJeremy Spewock
1156a91d5f47SJeremy Spewock    @classmethod
1157a91d5f47SJeremy Spewock    def from_str(cls, name: str) -> Self:
1158a91d5f47SJeremy Spewock        """Makes a flag matching the supplied name.
1159a91d5f47SJeremy Spewock
1160a91d5f47SJeremy Spewock        Args:
1161a91d5f47SJeremy Spewock            name: a valid member of this flag in text
1162a91d5f47SJeremy Spewock        Returns:
1163a91d5f47SJeremy Spewock            An instance of this flag.
1164a91d5f47SJeremy Spewock        """
1165a91d5f47SJeremy Spewock        member_name = name.strip().replace("-", "_")
1166a91d5f47SJeremy Spewock        return cls[member_name]
1167a91d5f47SJeremy Spewock
1168a91d5f47SJeremy Spewock    @classmethod
1169a91d5f47SJeremy Spewock    def make_parser(cls, hw: bool) -> ParserFn:
1170a91d5f47SJeremy Spewock        """Makes a parser function.
1171a91d5f47SJeremy Spewock
1172a91d5f47SJeremy Spewock        Args:
1173a91d5f47SJeremy Spewock            hw: Whether to make a parser for hardware ptypes or software ptypes. If :data:`True`,
1174a91d5f47SJeremy Spewock                hardware ptypes will be collected, otherwise software pytpes will.
1175a91d5f47SJeremy Spewock
1176a91d5f47SJeremy Spewock        Returns:
1177a91d5f47SJeremy Spewock            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a
1178a91d5f47SJeremy Spewock                parser function that makes an instance of this flag from text.
1179a91d5f47SJeremy Spewock        """
1180a91d5f47SJeremy Spewock        return TextParser.wrap(
1181a91d5f47SJeremy Spewock            TextParser.find(f"{'hw' if hw else 'sw'} ptype: ([^-]+)"),
1182a91d5f47SJeremy Spewock            cls.from_list_string,
1183a91d5f47SJeremy Spewock        )
1184a91d5f47SJeremy Spewock
1185a91d5f47SJeremy Spewock
1186a91d5f47SJeremy Spewock@dataclass
1187a91d5f47SJeremy Spewockclass TestPmdVerbosePacket(TextParser):
1188a91d5f47SJeremy Spewock    """Packet information provided by verbose output in Testpmd.
1189a91d5f47SJeremy Spewock
1190a91d5f47SJeremy Spewock    This dataclass expects that packet information be prepended with the starting line of packet
1191a91d5f47SJeremy Spewock    bursts. Specifically, the line that reads "port X/queue Y: sent/received Z packets".
1192a91d5f47SJeremy Spewock    """
1193a91d5f47SJeremy Spewock
1194a91d5f47SJeremy Spewock    #: ID of the port that handled the packet.
1195a91d5f47SJeremy Spewock    port_id: int = field(metadata=TextParser.find_int(r"port (\d+)/queue \d+"))
1196a91d5f47SJeremy Spewock    #: ID of the queue that handled the packet.
1197a91d5f47SJeremy Spewock    queue_id: int = field(metadata=TextParser.find_int(r"port \d+/queue (\d+)"))
1198a91d5f47SJeremy Spewock    #: Whether the packet was received or sent by the queue/port.
1199a91d5f47SJeremy Spewock    was_received: bool = field(metadata=TextParser.find(r"received \d+ packets"))
1200a91d5f47SJeremy Spewock    #:
1201a91d5f47SJeremy Spewock    src_mac: str = field(metadata=TextParser.find(f"src=({REGEX_FOR_MAC_ADDRESS})"))
1202a91d5f47SJeremy Spewock    #:
1203a91d5f47SJeremy Spewock    dst_mac: str = field(metadata=TextParser.find(f"dst=({REGEX_FOR_MAC_ADDRESS})"))
1204a91d5f47SJeremy Spewock    #: Memory pool the packet was handled on.
1205a91d5f47SJeremy Spewock    pool: str = field(metadata=TextParser.find(r"pool=(\S+)"))
1206a91d5f47SJeremy Spewock    #: Packet type in hex.
1207a91d5f47SJeremy Spewock    p_type: int = field(metadata=TextParser.find_int(r"type=(0x[a-fA-F\d]+)"))
1208a91d5f47SJeremy Spewock    #:
1209a91d5f47SJeremy Spewock    length: int = field(metadata=TextParser.find_int(r"length=(\d+)"))
1210a91d5f47SJeremy Spewock    #: Number of segments in the packet.
1211a91d5f47SJeremy Spewock    nb_segs: int = field(metadata=TextParser.find_int(r"nb_segs=(\d+)"))
1212a91d5f47SJeremy Spewock    #: Hardware packet type.
1213a91d5f47SJeremy Spewock    hw_ptype: RtePTypes = field(metadata=RtePTypes.make_parser(hw=True))
1214a91d5f47SJeremy Spewock    #: Software packet type.
1215a91d5f47SJeremy Spewock    sw_ptype: RtePTypes = field(metadata=RtePTypes.make_parser(hw=False))
1216a91d5f47SJeremy Spewock    #:
1217a91d5f47SJeremy Spewock    l2_len: int = field(metadata=TextParser.find_int(r"l2_len=(\d+)"))
1218a91d5f47SJeremy Spewock    #:
1219a91d5f47SJeremy Spewock    ol_flags: PacketOffloadFlag = field(metadata=PacketOffloadFlag.make_parser())
1220a91d5f47SJeremy Spewock    #: RSS hash of the packet in hex.
1221a91d5f47SJeremy Spewock    rss_hash: int | None = field(
1222a91d5f47SJeremy Spewock        default=None, metadata=TextParser.find_int(r"RSS hash=(0x[a-fA-F\d]+)")
1223a91d5f47SJeremy Spewock    )
1224a91d5f47SJeremy Spewock    #: RSS queue that handled the packet in hex.
1225a91d5f47SJeremy Spewock    rss_queue: int | None = field(
1226a91d5f47SJeremy Spewock        default=None, metadata=TextParser.find_int(r"RSS queue=(0x[a-fA-F\d]+)")
1227a91d5f47SJeremy Spewock    )
1228a91d5f47SJeremy Spewock    #:
1229a91d5f47SJeremy Spewock    l3_len: int | None = field(default=None, metadata=TextParser.find_int(r"l3_len=(\d+)"))
1230a91d5f47SJeremy Spewock    #:
1231a91d5f47SJeremy Spewock    l4_len: int | None = field(default=None, metadata=TextParser.find_int(r"l4_len=(\d+)"))
1232a91d5f47SJeremy Spewock
1233a91d5f47SJeremy Spewock
1234a79884f9SJuraj Linkešclass RxOffloadCapability(Flag):
1235a79884f9SJuraj Linkeš    """Rx offload capabilities of a device.
1236a79884f9SJuraj Linkeš
1237a79884f9SJuraj Linkeš    The flags are taken from ``lib/ethdev/rte_ethdev.h``.
1238a79884f9SJuraj Linkeš    They're prefixed with ``RTE_ETH_RX_OFFLOAD`` in ``lib/ethdev/rte_ethdev.h``
1239a79884f9SJuraj Linkeš    instead of ``RX_OFFLOAD``, which is what testpmd changes the prefix to.
1240a79884f9SJuraj Linkeš    The values are not contiguous, so the correspondence is preserved
1241a79884f9SJuraj Linkeš    by specifying concrete values interspersed between auto() values.
1242a79884f9SJuraj Linkeš
1243a79884f9SJuraj Linkeš    The ``RX_OFFLOAD`` prefix has been preserved so that the same flag names can be used
1244a79884f9SJuraj Linkeš    in :class:`NicCapability`. The prefix is needed in :class:`NicCapability` since there's
1245a79884f9SJuraj Linkeš    no other qualifier which would sufficiently distinguish it from other capabilities.
1246a79884f9SJuraj Linkeš
1247a79884f9SJuraj Linkeš    References:
1248a79884f9SJuraj Linkeš        DPDK lib: ``lib/ethdev/rte_ethdev.h``
1249a79884f9SJuraj Linkeš        testpmd display function: ``app/test-pmd/cmdline.c:print_rx_offloads()``
1250a79884f9SJuraj Linkeš    """
1251a79884f9SJuraj Linkeš
1252a79884f9SJuraj Linkeš    #:
1253a79884f9SJuraj Linkeš    RX_OFFLOAD_VLAN_STRIP = auto()
1254a79884f9SJuraj Linkeš    #: Device supports L3 checksum offload.
1255a79884f9SJuraj Linkeš    RX_OFFLOAD_IPV4_CKSUM = auto()
1256a79884f9SJuraj Linkeš    #: Device supports L4 checksum offload.
1257a79884f9SJuraj Linkeš    RX_OFFLOAD_UDP_CKSUM = auto()
1258a79884f9SJuraj Linkeš    #: Device supports L4 checksum offload.
1259a79884f9SJuraj Linkeš    RX_OFFLOAD_TCP_CKSUM = auto()
1260a79884f9SJuraj Linkeš    #: Device supports Large Receive Offload.
1261a79884f9SJuraj Linkeš    RX_OFFLOAD_TCP_LRO = auto()
1262a79884f9SJuraj Linkeš    #: Device supports QinQ (queue in queue) offload.
1263a79884f9SJuraj Linkeš    RX_OFFLOAD_QINQ_STRIP = auto()
1264a79884f9SJuraj Linkeš    #: Device supports inner packet L3 checksum.
1265a79884f9SJuraj Linkeš    RX_OFFLOAD_OUTER_IPV4_CKSUM = auto()
1266a79884f9SJuraj Linkeš    #: Device supports MACsec.
1267a79884f9SJuraj Linkeš    RX_OFFLOAD_MACSEC_STRIP = auto()
1268a79884f9SJuraj Linkeš    #: Device supports filtering of a VLAN Tag identifier.
1269a79884f9SJuraj Linkeš    RX_OFFLOAD_VLAN_FILTER = 1 << 9
1270a79884f9SJuraj Linkeš    #: Device supports VLAN offload.
1271a79884f9SJuraj Linkeš    RX_OFFLOAD_VLAN_EXTEND = auto()
1272a79884f9SJuraj Linkeš    #: Device supports receiving segmented mbufs.
1273a79884f9SJuraj Linkeš    RX_OFFLOAD_SCATTER = 1 << 13
1274a79884f9SJuraj Linkeš    #: Device supports Timestamp.
1275a79884f9SJuraj Linkeš    RX_OFFLOAD_TIMESTAMP = auto()
1276a79884f9SJuraj Linkeš    #: Device supports crypto processing while packet is received in NIC.
1277a79884f9SJuraj Linkeš    RX_OFFLOAD_SECURITY = auto()
1278a79884f9SJuraj Linkeš    #: Device supports CRC stripping.
1279a79884f9SJuraj Linkeš    RX_OFFLOAD_KEEP_CRC = auto()
1280a79884f9SJuraj Linkeš    #: Device supports L4 checksum offload.
1281a79884f9SJuraj Linkeš    RX_OFFLOAD_SCTP_CKSUM = auto()
1282a79884f9SJuraj Linkeš    #: Device supports inner packet L4 checksum.
1283a79884f9SJuraj Linkeš    RX_OFFLOAD_OUTER_UDP_CKSUM = auto()
1284a79884f9SJuraj Linkeš    #: Device supports RSS hashing.
1285a79884f9SJuraj Linkeš    RX_OFFLOAD_RSS_HASH = auto()
1286a79884f9SJuraj Linkeš    #: Device supports
1287a79884f9SJuraj Linkeš    RX_OFFLOAD_BUFFER_SPLIT = auto()
1288a79884f9SJuraj Linkeš    #: Device supports all checksum capabilities.
1289a79884f9SJuraj Linkeš    RX_OFFLOAD_CHECKSUM = RX_OFFLOAD_IPV4_CKSUM | RX_OFFLOAD_UDP_CKSUM | RX_OFFLOAD_TCP_CKSUM
1290a79884f9SJuraj Linkeš    #: Device supports all VLAN capabilities.
1291a79884f9SJuraj Linkeš    RX_OFFLOAD_VLAN = (
1292a79884f9SJuraj Linkeš        RX_OFFLOAD_VLAN_STRIP
1293a79884f9SJuraj Linkeš        | RX_OFFLOAD_VLAN_FILTER
1294a79884f9SJuraj Linkeš        | RX_OFFLOAD_VLAN_EXTEND
1295a79884f9SJuraj Linkeš        | RX_OFFLOAD_QINQ_STRIP
1296a79884f9SJuraj Linkeš    )
1297a79884f9SJuraj Linkeš
1298a79884f9SJuraj Linkeš    @classmethod
1299a79884f9SJuraj Linkeš    def from_string(cls, line: str) -> Self:
1300a79884f9SJuraj Linkeš        """Make an instance from a string containing the flag names separated with a space.
1301a79884f9SJuraj Linkeš
1302a79884f9SJuraj Linkeš        Args:
1303a79884f9SJuraj Linkeš            line: The line to parse.
1304a79884f9SJuraj Linkeš
1305a79884f9SJuraj Linkeš        Returns:
1306a79884f9SJuraj Linkeš            A new instance containing all found flags.
1307a79884f9SJuraj Linkeš        """
1308a79884f9SJuraj Linkeš        flag = cls(0)
1309a79884f9SJuraj Linkeš        for flag_name in line.split():
1310a79884f9SJuraj Linkeš            flag |= cls[f"RX_OFFLOAD_{flag_name}"]
1311a79884f9SJuraj Linkeš        return flag
1312a79884f9SJuraj Linkeš
1313a79884f9SJuraj Linkeš    @classmethod
1314a79884f9SJuraj Linkeš    def make_parser(cls, per_port: bool) -> ParserFn:
1315a79884f9SJuraj Linkeš        """Make a parser function.
1316a79884f9SJuraj Linkeš
1317a79884f9SJuraj Linkeš        Args:
1318a79884f9SJuraj Linkeš            per_port: If :data:`True`, will return capabilities per port. If :data:`False`,
1319a79884f9SJuraj Linkeš                will return capabilities per queue.
1320a79884f9SJuraj Linkeš
1321a79884f9SJuraj Linkeš        Returns:
1322a79884f9SJuraj Linkeš            ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a
1323a79884f9SJuraj Linkeš                parser function that makes an instance of this flag from text.
1324a79884f9SJuraj Linkeš        """
1325a79884f9SJuraj Linkeš        granularity = "Port" if per_port else "Queue"
1326a79884f9SJuraj Linkeš        return TextParser.wrap(
1327a79884f9SJuraj Linkeš            TextParser.find(rf"Per {granularity}\s+:(.*)$", re.MULTILINE),
1328a79884f9SJuraj Linkeš            cls.from_string,
1329a79884f9SJuraj Linkeš        )
1330a79884f9SJuraj Linkeš
1331a79884f9SJuraj Linkeš
1332a79884f9SJuraj Linkeš@dataclass
1333a79884f9SJuraj Linkešclass RxOffloadCapabilities(TextParser):
1334a79884f9SJuraj Linkeš    """The result of testpmd's ``show port <port_id> rx_offload capabilities`` command.
1335a79884f9SJuraj Linkeš
1336a79884f9SJuraj Linkeš    References:
1337a79884f9SJuraj Linkeš        testpmd command function: ``app/test-pmd/cmdline.c:cmd_rx_offload_get_capa()``
1338a79884f9SJuraj Linkeš        testpmd display function: ``app/test-pmd/cmdline.c:cmd_rx_offload_get_capa_parsed()``
1339a79884f9SJuraj Linkeš    """
1340a79884f9SJuraj Linkeš
1341a79884f9SJuraj Linkeš    #:
1342a79884f9SJuraj Linkeš    port_id: int = field(
1343a79884f9SJuraj Linkeš        metadata=TextParser.find_int(r"Rx Offloading Capabilities of port (\d+) :")
1344a79884f9SJuraj Linkeš    )
1345a79884f9SJuraj Linkeš    #: Per-queue Rx offload capabilities.
1346a79884f9SJuraj Linkeš    per_queue: RxOffloadCapability = field(metadata=RxOffloadCapability.make_parser(False))
1347a79884f9SJuraj Linkeš    #: Capabilities other than per-queue Rx offload capabilities.
1348a79884f9SJuraj Linkeš    per_port: RxOffloadCapability = field(metadata=RxOffloadCapability.make_parser(True))
1349a79884f9SJuraj Linkeš
1350a79884f9SJuraj Linkeš
13512ecfd126SLuca Vizzarrodef requires_stopped_ports(func: TestPmdShellMethod) -> TestPmdShellMethod:
13522ecfd126SLuca Vizzarro    """Decorator for :class:`TestPmdShell` commands methods that require stopped ports.
13532ecfd126SLuca Vizzarro
13542ecfd126SLuca Vizzarro    If the decorated method is called while the ports are started, then these are stopped before
13552ecfd126SLuca Vizzarro    continuing.
13562ecfd126SLuca Vizzarro
13572ecfd126SLuca Vizzarro    Args:
13582ecfd126SLuca Vizzarro        func: The :class:`TestPmdShell` method to decorate.
13592ecfd126SLuca Vizzarro    """
13602ecfd126SLuca Vizzarro
13612ecfd126SLuca Vizzarro    @functools.wraps(func)
13622ecfd126SLuca Vizzarro    def _wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs):
13632ecfd126SLuca Vizzarro        if self.ports_started:
13642ecfd126SLuca Vizzarro            self._logger.debug("Ports need to be stopped to continue.")
13652ecfd126SLuca Vizzarro            self.stop_all_ports()
13662ecfd126SLuca Vizzarro
13672ecfd126SLuca Vizzarro        return func(self, *args, **kwargs)
13682ecfd126SLuca Vizzarro
13692ecfd126SLuca Vizzarro    return _wrapper
13702ecfd126SLuca Vizzarro
13712ecfd126SLuca Vizzarro
13722ecfd126SLuca Vizzarrodef requires_started_ports(func: TestPmdShellMethod) -> TestPmdShellMethod:
13732ecfd126SLuca Vizzarro    """Decorator for :class:`TestPmdShell` commands methods that require started ports.
13742ecfd126SLuca Vizzarro
13752ecfd126SLuca Vizzarro    If the decorated method is called while the ports are stopped, then these are started before
13762ecfd126SLuca Vizzarro    continuing.
13772ecfd126SLuca Vizzarro
13782ecfd126SLuca Vizzarro    Args:
13792ecfd126SLuca Vizzarro        func: The :class:`TestPmdShell` method to decorate.
13802ecfd126SLuca Vizzarro    """
13812ecfd126SLuca Vizzarro
13822ecfd126SLuca Vizzarro    @functools.wraps(func)
13832ecfd126SLuca Vizzarro    def _wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs):
13842ecfd126SLuca Vizzarro        if not self.ports_started:
13852ecfd126SLuca Vizzarro            self._logger.debug("Ports need to be started to continue.")
13862ecfd126SLuca Vizzarro            self.start_all_ports()
13872ecfd126SLuca Vizzarro
13882ecfd126SLuca Vizzarro        return func(self, *args, **kwargs)
13892ecfd126SLuca Vizzarro
13902ecfd126SLuca Vizzarro    return _wrapper
13912ecfd126SLuca Vizzarro
13922ecfd126SLuca Vizzarro
1393c0119400SJuraj Linkešdef add_remove_mtu(mtu: int = 1500) -> Callable[[TestPmdShellMethod], TestPmdShellMethod]:
1394c0119400SJuraj Linkeš    """Configure MTU to `mtu` on all ports, run the decorated function, then revert.
1395c0119400SJuraj Linkeš
1396c0119400SJuraj Linkeš    Args:
1397c0119400SJuraj Linkeš        mtu: The MTU to configure all ports on.
1398c0119400SJuraj Linkeš
1399c0119400SJuraj Linkeš    Returns:
1400c0119400SJuraj Linkeš        The method decorated with setting and reverting MTU.
1401c0119400SJuraj Linkeš    """
1402c0119400SJuraj Linkeš
1403c0119400SJuraj Linkeš    def decorator(func: TestPmdShellMethod) -> TestPmdShellMethod:
1404c0119400SJuraj Linkeš        @functools.wraps(func)
1405c0119400SJuraj Linkeš        def wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs):
1406c0119400SJuraj Linkeš            original_mtu = self.ports[0].mtu
1407c0119400SJuraj Linkeš            self.set_port_mtu_all(mtu=mtu, verify=False)
1408c0119400SJuraj Linkeš            retval = func(self, *args, **kwargs)
1409c0119400SJuraj Linkeš            self.set_port_mtu_all(original_mtu if original_mtu else 1500, verify=False)
1410c0119400SJuraj Linkeš            return retval
1411c0119400SJuraj Linkeš
1412c0119400SJuraj Linkeš        return wrapper
1413c0119400SJuraj Linkeš
1414c0119400SJuraj Linkeš    return decorator
1415c0119400SJuraj Linkeš
1416c0119400SJuraj Linkeš
1417bfad0948SLuca Vizzarroclass TestPmdShell(DPDKShell):
14186ef07151SJuraj Linkeš    """Testpmd interactive shell.
14196ef07151SJuraj Linkeš
14206ef07151SJuraj Linkeš    The testpmd shell users should never use
14216ef07151SJuraj Linkeš    the :meth:`~.interactive_shell.InteractiveShell.send_command` method directly, but rather
14226ef07151SJuraj Linkeš    call specialized methods. If there isn't one that satisfies a need, it should be added.
14232ecfd126SLuca Vizzarro
14242ecfd126SLuca Vizzarro    Attributes:
14252ecfd126SLuca Vizzarro        ports_started: Indicates whether the ports are started.
14266ef07151SJuraj Linkeš    """
14276ef07151SJuraj Linkeš
1428bfad0948SLuca Vizzarro    _app_params: TestPmdParams
142985d15c7cSJuraj Linkeš    _ports: list[TestPmdPort] | None
1430369d34b8SJeremy Spewock
14316ef07151SJuraj Linkeš    #: The path to the testpmd executable.
14326ef07151SJuraj Linkeš    path: ClassVar[PurePath] = PurePath("app", "dpdk-testpmd")
14336ef07151SJuraj Linkeš
14346ef07151SJuraj Linkeš    #: The testpmd's prompt.
14356ef07151SJuraj Linkeš    _default_prompt: ClassVar[str] = "testpmd>"
14366ef07151SJuraj Linkeš
14376ef07151SJuraj Linkeš    #: This forces the prompt to appear after sending a command.
14386ef07151SJuraj Linkeš    _command_extra_chars: ClassVar[str] = "\n"
1439840b1e01SJuraj Linkeš
14402ecfd126SLuca Vizzarro    ports_started: bool
14412ecfd126SLuca Vizzarro
1442bfad0948SLuca Vizzarro    def __init__(
1443bfad0948SLuca Vizzarro        self,
1444bfad0948SLuca Vizzarro        node: SutNode,
1445bfad0948SLuca Vizzarro        privileged: bool = True,
1446bfad0948SLuca Vizzarro        timeout: float = SETTINGS.timeout,
1447bfad0948SLuca Vizzarro        lcore_filter_specifier: LogicalCoreCount | LogicalCoreList = LogicalCoreCount(),
1448bfad0948SLuca Vizzarro        ascending_cores: bool = True,
1449bfad0948SLuca Vizzarro        append_prefix_timestamp: bool = True,
145065a1b4e8SJeremy Spewock        name: str | None = None,
145187ba4cdcSLuca Vizzarro        **app_params: Unpack[TestPmdParamsDict],
1452bfad0948SLuca Vizzarro    ) -> None:
1453bfad0948SLuca Vizzarro        """Overrides :meth:`~.dpdk_shell.DPDKShell.__init__`. Changes app_params to kwargs."""
1454bfad0948SLuca Vizzarro        super().__init__(
1455bfad0948SLuca Vizzarro            node,
1456bfad0948SLuca Vizzarro            privileged,
1457bfad0948SLuca Vizzarro            timeout,
1458bfad0948SLuca Vizzarro            lcore_filter_specifier,
1459bfad0948SLuca Vizzarro            ascending_cores,
1460bfad0948SLuca Vizzarro            append_prefix_timestamp,
1461bfad0948SLuca Vizzarro            TestPmdParams(**app_params),
146265a1b4e8SJeremy Spewock            name,
1463fd8cd8eeSLuca Vizzarro        )
14642ecfd126SLuca Vizzarro        self.ports_started = not self._app_params.disable_device_start
146585d15c7cSJuraj Linkeš        self._ports = None
146685d15c7cSJuraj Linkeš
146785d15c7cSJuraj Linkeš    @property
146885d15c7cSJuraj Linkeš    def ports(self) -> list[TestPmdPort]:
146985d15c7cSJuraj Linkeš        """The ports of the instance.
147085d15c7cSJuraj Linkeš
147185d15c7cSJuraj Linkeš        This caches the ports returned by :meth:`show_port_info_all`.
147285d15c7cSJuraj Linkeš        To force an update of port information, execute :meth:`show_port_info_all` or
147385d15c7cSJuraj Linkeš        :meth:`show_port_info`.
147485d15c7cSJuraj Linkeš
147585d15c7cSJuraj Linkeš        Returns: The list of known testpmd ports.
147685d15c7cSJuraj Linkeš        """
147785d15c7cSJuraj Linkeš        if self._ports is None:
147885d15c7cSJuraj Linkeš            return self.show_port_info_all()
147985d15c7cSJuraj Linkeš        return self._ports
14802ecfd126SLuca Vizzarro
14812ecfd126SLuca Vizzarro    @requires_started_ports
1482369d34b8SJeremy Spewock    def start(self, verify: bool = True) -> None:
1483369d34b8SJeremy Spewock        """Start packet forwarding with the current configuration.
1484369d34b8SJeremy Spewock
1485369d34b8SJeremy Spewock        Args:
1486369d34b8SJeremy Spewock            verify: If :data:`True` , a second start command will be sent in an attempt to verify
1487369d34b8SJeremy Spewock                packet forwarding started as expected.
1488369d34b8SJeremy Spewock
1489369d34b8SJeremy Spewock        Raises:
1490369d34b8SJeremy Spewock            InteractiveCommandExecutionError: If `verify` is :data:`True` and forwarding fails to
1491369d34b8SJeremy Spewock                start or ports fail to come up.
1492369d34b8SJeremy Spewock        """
1493369d34b8SJeremy Spewock        self.send_command("start")
1494369d34b8SJeremy Spewock        if verify:
1495369d34b8SJeremy Spewock            # If forwarding was already started, sending "start" again should tell us
1496369d34b8SJeremy Spewock            start_cmd_output = self.send_command("start")
1497369d34b8SJeremy Spewock            if "Packet forwarding already started" not in start_cmd_output:
1498369d34b8SJeremy Spewock                self._logger.debug(f"Failed to start packet forwarding: \n{start_cmd_output}")
1499369d34b8SJeremy Spewock                raise InteractiveCommandExecutionError("Testpmd failed to start packet forwarding.")
1500369d34b8SJeremy Spewock
15010264e408SLuca Vizzarro            number_of_ports = len(self._app_params.allowed_ports or [])
1502bfad0948SLuca Vizzarro            for port_id in range(number_of_ports):
1503369d34b8SJeremy Spewock                if not self.wait_link_status_up(port_id):
1504369d34b8SJeremy Spewock                    raise InteractiveCommandExecutionError(
1505369d34b8SJeremy Spewock                        "Not all ports came up after starting packet forwarding in testpmd."
1506369d34b8SJeremy Spewock                    )
1507369d34b8SJeremy Spewock
1508a91d5f47SJeremy Spewock    def stop(self, verify: bool = True) -> str:
1509369d34b8SJeremy Spewock        """Stop packet forwarding.
1510369d34b8SJeremy Spewock
1511369d34b8SJeremy Spewock        Args:
1512369d34b8SJeremy Spewock            verify: If :data:`True` , the output of the stop command is scanned to verify that
1513369d34b8SJeremy Spewock                forwarding was stopped successfully or not started. If neither is found, it is
1514369d34b8SJeremy Spewock                considered an error.
1515369d34b8SJeremy Spewock
1516369d34b8SJeremy Spewock        Raises:
1517369d34b8SJeremy Spewock            InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
1518369d34b8SJeremy Spewock                forwarding results in an error.
1519a91d5f47SJeremy Spewock
1520a91d5f47SJeremy Spewock        Returns:
1521a91d5f47SJeremy Spewock            Output gathered from the stop command and all other preceding logs in the buffer. This
1522a91d5f47SJeremy Spewock            output is most often used to view forwarding statistics that are displayed when this
1523a91d5f47SJeremy Spewock            command is sent as well as any verbose packet information that hasn't been consumed
1524a91d5f47SJeremy Spewock            prior to calling this method.
1525369d34b8SJeremy Spewock        """
1526369d34b8SJeremy Spewock        stop_cmd_output = self.send_command("stop")
1527369d34b8SJeremy Spewock        if verify:
1528369d34b8SJeremy Spewock            if (
1529369d34b8SJeremy Spewock                "Done." not in stop_cmd_output
1530369d34b8SJeremy Spewock                and "Packet forwarding not started" not in stop_cmd_output
1531369d34b8SJeremy Spewock            ):
1532369d34b8SJeremy Spewock                self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}")
1533369d34b8SJeremy Spewock                raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.")
1534a91d5f47SJeremy Spewock        return stop_cmd_output
1535369d34b8SJeremy Spewock
1536840b1e01SJuraj Linkeš    def get_devices(self) -> list[TestPmdDevice]:
15376ef07151SJuraj Linkeš        """Get a list of device names that are known to testpmd.
1538840b1e01SJuraj Linkeš
15396ef07151SJuraj Linkeš        Uses the device info listed in testpmd and then parses the output.
1540840b1e01SJuraj Linkeš
1541840b1e01SJuraj Linkeš        Returns:
15426ef07151SJuraj Linkeš            A list of devices.
1543840b1e01SJuraj Linkeš        """
1544840b1e01SJuraj Linkeš        dev_info: str = self.send_command("show device info all")
1545840b1e01SJuraj Linkeš        dev_list: list[TestPmdDevice] = []
1546840b1e01SJuraj Linkeš        for line in dev_info.split("\n"):
1547840b1e01SJuraj Linkeš            if "device name:" in line.lower():
1548840b1e01SJuraj Linkeš                dev_list.append(TestPmdDevice(line))
1549840b1e01SJuraj Linkeš        return dev_list
1550369d34b8SJeremy Spewock
1551369d34b8SJeremy Spewock    def wait_link_status_up(self, port_id: int, timeout=SETTINGS.timeout) -> bool:
1552369d34b8SJeremy Spewock        """Wait until the link status on the given port is "up".
1553369d34b8SJeremy Spewock
1554369d34b8SJeremy Spewock        Arguments:
1555369d34b8SJeremy Spewock            port_id: Port to check the link status on.
1556369d34b8SJeremy Spewock            timeout: Time to wait for the link to come up. The default value for this
1557369d34b8SJeremy Spewock                argument may be modified using the :option:`--timeout` command-line argument
1558369d34b8SJeremy Spewock                or the :envvar:`DTS_TIMEOUT` environment variable.
1559369d34b8SJeremy Spewock
1560369d34b8SJeremy Spewock        Returns:
1561369d34b8SJeremy Spewock            Whether the link came up in time or not.
1562369d34b8SJeremy Spewock        """
1563369d34b8SJeremy Spewock        time_to_stop = time.time() + timeout
1564369d34b8SJeremy Spewock        port_info: str = ""
1565369d34b8SJeremy Spewock        while time.time() < time_to_stop:
1566369d34b8SJeremy Spewock            port_info = self.send_command(f"show port info {port_id}")
1567369d34b8SJeremy Spewock            if "Link status: up" in port_info:
1568369d34b8SJeremy Spewock                break
1569369d34b8SJeremy Spewock            time.sleep(0.5)
1570369d34b8SJeremy Spewock        else:
1571369d34b8SJeremy Spewock            self._logger.error(f"The link for port {port_id} did not come up in the given timeout.")
1572369d34b8SJeremy Spewock        return "Link status: up" in port_info
1573369d34b8SJeremy Spewock
1574fc0f7dc4SLuca Vizzarro    def set_forward_mode(self, mode: SimpleForwardingModes, verify: bool = True):
1575369d34b8SJeremy Spewock        """Set packet forwarding mode.
1576369d34b8SJeremy Spewock
1577369d34b8SJeremy Spewock        Args:
1578369d34b8SJeremy Spewock            mode: The forwarding mode to use.
1579369d34b8SJeremy Spewock            verify: If :data:`True` the output of the command will be scanned in an attempt to
1580369d34b8SJeremy Spewock                verify that the forwarding mode was set to `mode` properly.
1581369d34b8SJeremy Spewock
1582369d34b8SJeremy Spewock        Raises:
1583369d34b8SJeremy Spewock            InteractiveCommandExecutionError: If `verify` is :data:`True` and the forwarding mode
1584369d34b8SJeremy Spewock                fails to update.
1585369d34b8SJeremy Spewock        """
1586369d34b8SJeremy Spewock        set_fwd_output = self.send_command(f"set fwd {mode.value}")
1587d7181426SDean Marx        if verify:
1588369d34b8SJeremy Spewock            if f"Set {mode.value} packet forwarding mode" not in set_fwd_output:
1589369d34b8SJeremy Spewock                self._logger.debug(f"Failed to set fwd mode to {mode.value}:\n{set_fwd_output}")
1590369d34b8SJeremy Spewock                raise InteractiveCommandExecutionError(
1591369d34b8SJeremy Spewock                    f"Test pmd failed to set fwd mode to {mode.value}"
1592369d34b8SJeremy Spewock                )
1593369d34b8SJeremy Spewock
15942ecfd126SLuca Vizzarro    def stop_all_ports(self, verify: bool = True) -> None:
15952ecfd126SLuca Vizzarro        """Stops all the ports.
15962ecfd126SLuca Vizzarro
15972ecfd126SLuca Vizzarro        Args:
15982ecfd126SLuca Vizzarro            verify: If :data:`True`, the output of the command will be checked for a successful
15992ecfd126SLuca Vizzarro                execution.
16002ecfd126SLuca Vizzarro
16012ecfd126SLuca Vizzarro        Raises:
16022ecfd126SLuca Vizzarro            InteractiveCommandExecutionError: If `verify` is :data:`True` and the ports were not
16032ecfd126SLuca Vizzarro                stopped successfully.
16042ecfd126SLuca Vizzarro        """
16052ecfd126SLuca Vizzarro        self._logger.debug("Stopping all the ports...")
16062ecfd126SLuca Vizzarro        output = self.send_command("port stop all")
16072ecfd126SLuca Vizzarro        if verify and not output.strip().endswith("Done"):
16082ecfd126SLuca Vizzarro            raise InteractiveCommandExecutionError("Ports were not stopped successfully.")
16092ecfd126SLuca Vizzarro
16102ecfd126SLuca Vizzarro        self.ports_started = False
16112ecfd126SLuca Vizzarro
16122ecfd126SLuca Vizzarro    def start_all_ports(self, verify: bool = True) -> None:
16132ecfd126SLuca Vizzarro        """Starts all the ports.
16142ecfd126SLuca Vizzarro
16152ecfd126SLuca Vizzarro        Args:
16162ecfd126SLuca Vizzarro            verify: If :data:`True`, the output of the command will be checked for a successful
16172ecfd126SLuca Vizzarro                execution.
16182ecfd126SLuca Vizzarro
16192ecfd126SLuca Vizzarro        Raises:
16202ecfd126SLuca Vizzarro            InteractiveCommandExecutionError: If `verify` is :data:`True` and the ports were not
16212ecfd126SLuca Vizzarro                started successfully.
16222ecfd126SLuca Vizzarro        """
16232ecfd126SLuca Vizzarro        self._logger.debug("Starting all the ports...")
16242ecfd126SLuca Vizzarro        output = self.send_command("port start all")
16252ecfd126SLuca Vizzarro        if verify and not output.strip().endswith("Done"):
16262ecfd126SLuca Vizzarro            raise InteractiveCommandExecutionError("Ports were not started successfully.")
16272ecfd126SLuca Vizzarro
16282ecfd126SLuca Vizzarro        self.ports_started = True
16292ecfd126SLuca Vizzarro
163007816eadSLuca Vizzarro    @requires_stopped_ports
163107816eadSLuca Vizzarro    def set_ports_queues(self, number_of: int) -> None:
163207816eadSLuca Vizzarro        """Sets the number of queues per port.
163307816eadSLuca Vizzarro
163407816eadSLuca Vizzarro        Args:
163507816eadSLuca Vizzarro            number_of: The number of RX/TX queues to create per port.
163607816eadSLuca Vizzarro
163707816eadSLuca Vizzarro        Raises:
163807816eadSLuca Vizzarro            InternalError: If `number_of` is invalid.
163907816eadSLuca Vizzarro        """
164007816eadSLuca Vizzarro        if number_of < 1:
164107816eadSLuca Vizzarro            raise InternalError("The number of queues must be positive and non-zero.")
164207816eadSLuca Vizzarro
164307816eadSLuca Vizzarro        self.send_command(f"port config all rxq {number_of}")
164407816eadSLuca Vizzarro        self.send_command(f"port config all txq {number_of}")
164507816eadSLuca Vizzarro
164661d5bc9bSLuca Vizzarro    def show_port_info_all(self) -> list[TestPmdPort]:
164761d5bc9bSLuca Vizzarro        """Returns the information of all the ports.
164861d5bc9bSLuca Vizzarro
164961d5bc9bSLuca Vizzarro        Returns:
165061d5bc9bSLuca Vizzarro            list[TestPmdPort]: A list containing all the ports information as `TestPmdPort`.
165161d5bc9bSLuca Vizzarro        """
165261d5bc9bSLuca Vizzarro        output = self.send_command("show port info all")
165361d5bc9bSLuca Vizzarro
165461d5bc9bSLuca Vizzarro        # Sample output of the "all" command looks like:
165561d5bc9bSLuca Vizzarro        #
165661d5bc9bSLuca Vizzarro        # <start>
165761d5bc9bSLuca Vizzarro        #
165861d5bc9bSLuca Vizzarro        #   ********************* Infos for port 0 *********************
165961d5bc9bSLuca Vizzarro        #   Key: value
166061d5bc9bSLuca Vizzarro        #
166161d5bc9bSLuca Vizzarro        #   ********************* Infos for port 1 *********************
166261d5bc9bSLuca Vizzarro        #   Key: value
166361d5bc9bSLuca Vizzarro        # <end>
166461d5bc9bSLuca Vizzarro        #
166561d5bc9bSLuca Vizzarro        # Takes advantage of the double new line in between ports as end delimiter. But we need to
166661d5bc9bSLuca Vizzarro        # artificially add a new line at the end to pick up the last port. Because commands are
166761d5bc9bSLuca Vizzarro        # executed on a pseudo-terminal created by paramiko on the remote node, lines end with CRLF.
166861d5bc9bSLuca Vizzarro        # Therefore we also need to take the carriage return into account.
166961d5bc9bSLuca Vizzarro        iter = re.finditer(r"\*{21}.*?[\r\n]{4}", output + "\r\n", re.S)
167085d15c7cSJuraj Linkeš        self._ports = [TestPmdPort.parse(block.group(0)) for block in iter]
167185d15c7cSJuraj Linkeš        return self._ports
167261d5bc9bSLuca Vizzarro
167361d5bc9bSLuca Vizzarro    def show_port_info(self, port_id: int) -> TestPmdPort:
167461d5bc9bSLuca Vizzarro        """Returns the given port information.
167561d5bc9bSLuca Vizzarro
167661d5bc9bSLuca Vizzarro        Args:
167761d5bc9bSLuca Vizzarro            port_id: The port ID to gather information for.
167861d5bc9bSLuca Vizzarro
167961d5bc9bSLuca Vizzarro        Raises:
168061d5bc9bSLuca Vizzarro            InteractiveCommandExecutionError: If `port_id` is invalid.
168161d5bc9bSLuca Vizzarro
168261d5bc9bSLuca Vizzarro        Returns:
168361d5bc9bSLuca Vizzarro            TestPmdPort: An instance of `TestPmdPort` containing the given port's information.
168461d5bc9bSLuca Vizzarro        """
168561d5bc9bSLuca Vizzarro        output = self.send_command(f"show port info {port_id}", skip_first_line=True)
168661d5bc9bSLuca Vizzarro        if output.startswith("Invalid port"):
168761d5bc9bSLuca Vizzarro            raise InteractiveCommandExecutionError("invalid port given")
168861d5bc9bSLuca Vizzarro
168985d15c7cSJuraj Linkeš        port = TestPmdPort.parse(output)
169085d15c7cSJuraj Linkeš        self._update_port(port)
169185d15c7cSJuraj Linkeš        return port
169285d15c7cSJuraj Linkeš
169385d15c7cSJuraj Linkeš    def _update_port(self, port: TestPmdPort) -> None:
169485d15c7cSJuraj Linkeš        if self._ports:
169585d15c7cSJuraj Linkeš            self._ports = [
169685d15c7cSJuraj Linkeš                existing_port if port.id != existing_port.id else port
169785d15c7cSJuraj Linkeš                for existing_port in self._ports
169885d15c7cSJuraj Linkeš            ]
169961d5bc9bSLuca Vizzarro
1700e3b16f45SNicholas Pratte    def set_mac_addr(self, port_id: int, mac_address: str, add: bool, verify: bool = True) -> None:
1701e3b16f45SNicholas Pratte        """Add or remove a mac address on a given port's Allowlist.
1702e3b16f45SNicholas Pratte
1703e3b16f45SNicholas Pratte        Args:
1704e3b16f45SNicholas Pratte            port_id: The port ID the mac address is set on.
1705e3b16f45SNicholas Pratte            mac_address: The mac address to be added to or removed from the specified port.
1706e3b16f45SNicholas Pratte            add: If :data:`True`, add the specified mac address. If :data:`False`, remove specified
1707e3b16f45SNicholas Pratte                mac address.
1708e3b16f45SNicholas Pratte            verify: If :data:'True', assert that the 'mac_addr' operation was successful. If
1709e3b16f45SNicholas Pratte                :data:'False', run the command and skip this assertion.
1710e3b16f45SNicholas Pratte
1711e3b16f45SNicholas Pratte        Raises:
1712e3b16f45SNicholas Pratte            InteractiveCommandExecutionError: If the set mac address operation fails.
1713e3b16f45SNicholas Pratte        """
1714e3b16f45SNicholas Pratte        mac_cmd = "add" if add else "remove"
1715e3b16f45SNicholas Pratte        output = self.send_command(f"mac_addr {mac_cmd} {port_id} {mac_address}")
1716e3b16f45SNicholas Pratte        if "Bad arguments" in output:
1717e3b16f45SNicholas Pratte            self._logger.debug("Invalid argument provided to mac_addr")
1718e3b16f45SNicholas Pratte            raise InteractiveCommandExecutionError("Invalid argument provided")
1719e3b16f45SNicholas Pratte
1720e3b16f45SNicholas Pratte        if verify:
1721e3b16f45SNicholas Pratte            if "mac_addr_cmd error:" in output:
1722e3b16f45SNicholas Pratte                self._logger.debug(f"Failed to {mac_cmd} {mac_address} on port {port_id}")
1723e3b16f45SNicholas Pratte                raise InteractiveCommandExecutionError(
1724e3b16f45SNicholas Pratte                    f"Failed to {mac_cmd} {mac_address} on port {port_id} \n{output}"
1725e3b16f45SNicholas Pratte                )
1726e3b16f45SNicholas Pratte
1727e3b16f45SNicholas Pratte    def set_multicast_mac_addr(
1728e3b16f45SNicholas Pratte        self, port_id: int, multi_addr: str, add: bool, verify: bool = True
1729e3b16f45SNicholas Pratte    ) -> None:
1730e3b16f45SNicholas Pratte        """Add or remove multicast mac address to a specified port's allow list.
1731e3b16f45SNicholas Pratte
1732e3b16f45SNicholas Pratte        Args:
1733e3b16f45SNicholas Pratte            port_id: The port ID the multicast address is set on.
1734e3b16f45SNicholas Pratte            multi_addr: The multicast address to be added or removed from the filter.
1735e3b16f45SNicholas Pratte            add: If :data:'True', add the specified multicast address to the port filter.
1736e3b16f45SNicholas Pratte                If :data:'False', remove the specified multicast address from the port filter.
1737e3b16f45SNicholas Pratte            verify: If :data:'True', assert that the 'mcast_addr' operations was successful.
1738e3b16f45SNicholas Pratte                If :data:'False', execute the 'mcast_addr' operation and skip the assertion.
1739e3b16f45SNicholas Pratte
1740e3b16f45SNicholas Pratte        Raises:
1741e3b16f45SNicholas Pratte            InteractiveCommandExecutionError: If either the 'add' or 'remove' operations fails.
1742e3b16f45SNicholas Pratte        """
1743e3b16f45SNicholas Pratte        mcast_cmd = "add" if add else "remove"
1744e3b16f45SNicholas Pratte        output = self.send_command(f"mcast_addr {mcast_cmd} {port_id} {multi_addr}")
1745e3b16f45SNicholas Pratte        if "Bad arguments" in output:
1746e3b16f45SNicholas Pratte            self._logger.debug("Invalid arguments provided to mcast_addr")
1747e3b16f45SNicholas Pratte            raise InteractiveCommandExecutionError("Invalid argument provided")
1748e3b16f45SNicholas Pratte
1749e3b16f45SNicholas Pratte        if verify:
1750e3b16f45SNicholas Pratte            if (
1751e3b16f45SNicholas Pratte                "Invalid multicast_addr" in output
1752e3b16f45SNicholas Pratte                or f'multicast address {"already" if add else "not"} filtered by port' in output
1753e3b16f45SNicholas Pratte            ):
1754e3b16f45SNicholas Pratte                self._logger.debug(f"Failed to {mcast_cmd} {multi_addr} on port {port_id}")
1755e3b16f45SNicholas Pratte                raise InteractiveCommandExecutionError(
1756e3b16f45SNicholas Pratte                    f"Failed to {mcast_cmd} {multi_addr} on port {port_id} \n{output}"
1757e3b16f45SNicholas Pratte                )
1758e3b16f45SNicholas Pratte
175953eacf3dSLuca Vizzarro    def show_port_stats_all(self) -> list[TestPmdPortStats]:
176053eacf3dSLuca Vizzarro        """Returns the statistics of all the ports.
176153eacf3dSLuca Vizzarro
176253eacf3dSLuca Vizzarro        Returns:
176353eacf3dSLuca Vizzarro            list[TestPmdPortStats]: A list containing all the ports stats as `TestPmdPortStats`.
176453eacf3dSLuca Vizzarro        """
176553eacf3dSLuca Vizzarro        output = self.send_command("show port stats all")
176653eacf3dSLuca Vizzarro
176753eacf3dSLuca Vizzarro        # Sample output of the "all" command looks like:
176853eacf3dSLuca Vizzarro        #
176953eacf3dSLuca Vizzarro        #   ########### NIC statistics for port 0 ###########
177053eacf3dSLuca Vizzarro        #   values...
177153eacf3dSLuca Vizzarro        #   #################################################
177253eacf3dSLuca Vizzarro        #
177353eacf3dSLuca Vizzarro        #   ########### NIC statistics for port 1 ###########
177453eacf3dSLuca Vizzarro        #   values...
177553eacf3dSLuca Vizzarro        #   #################################################
177653eacf3dSLuca Vizzarro        #
177753eacf3dSLuca Vizzarro        iter = re.finditer(r"(^  #*.+#*$[^#]+)^  #*\r$", output, re.MULTILINE)
177853eacf3dSLuca Vizzarro        return [TestPmdPortStats.parse(block.group(1)) for block in iter]
177953eacf3dSLuca Vizzarro
178053eacf3dSLuca Vizzarro    def show_port_stats(self, port_id: int) -> TestPmdPortStats:
178153eacf3dSLuca Vizzarro        """Returns the given port statistics.
178253eacf3dSLuca Vizzarro
178353eacf3dSLuca Vizzarro        Args:
178453eacf3dSLuca Vizzarro            port_id: The port ID to gather information for.
178553eacf3dSLuca Vizzarro
178653eacf3dSLuca Vizzarro        Raises:
178753eacf3dSLuca Vizzarro            InteractiveCommandExecutionError: If `port_id` is invalid.
178853eacf3dSLuca Vizzarro
178953eacf3dSLuca Vizzarro        Returns:
179053eacf3dSLuca Vizzarro            TestPmdPortStats: An instance of `TestPmdPortStats` containing the given port's stats.
179153eacf3dSLuca Vizzarro        """
179253eacf3dSLuca Vizzarro        output = self.send_command(f"show port stats {port_id}", skip_first_line=True)
179353eacf3dSLuca Vizzarro        if output.startswith("Invalid port"):
179453eacf3dSLuca Vizzarro            raise InteractiveCommandExecutionError("invalid port given")
179553eacf3dSLuca Vizzarro
179653eacf3dSLuca Vizzarro        return TestPmdPortStats.parse(output)
179753eacf3dSLuca Vizzarro
17989910db35SJeremy Spewock    @requires_stopped_ports
1799*bee7cf82SDean Marx    def csum_set_hw(
1800*bee7cf82SDean Marx        self, layers: ChecksumOffloadOptions, port_id: int, verify: bool = True
1801*bee7cf82SDean Marx    ) -> None:
1802*bee7cf82SDean Marx        """Enables hardware checksum offloading on the specified layer.
1803*bee7cf82SDean Marx
1804*bee7cf82SDean Marx        Args:
1805*bee7cf82SDean Marx            layers: The layer/layers that checksum offloading should be enabled on.
1806*bee7cf82SDean Marx            port_id: The port number to enable checksum offloading on, should be within 0-32.
1807*bee7cf82SDean Marx            verify: If :data:`True` the output of the command will be scanned in an attempt to
1808*bee7cf82SDean Marx                verify that checksum offloading was enabled on the port.
1809*bee7cf82SDean Marx
1810*bee7cf82SDean Marx        Raises:
1811*bee7cf82SDean Marx            InteractiveCommandExecutionError: If checksum offload is not enabled successfully.
1812*bee7cf82SDean Marx        """
1813*bee7cf82SDean Marx        for name, offload in ChecksumOffloadOptions.__members__.items():
1814*bee7cf82SDean Marx            if offload in layers:
1815*bee7cf82SDean Marx                name = name.replace("_", "-")
1816*bee7cf82SDean Marx                csum_output = self.send_command(f"csum set {name} hw {port_id}")
1817*bee7cf82SDean Marx                if verify:
1818*bee7cf82SDean Marx                    if (
1819*bee7cf82SDean Marx                        "Bad arguments" in csum_output
1820*bee7cf82SDean Marx                        or f"Please stop port {port_id} first" in csum_output
1821*bee7cf82SDean Marx                        or f"checksum offload is not supported by port {port_id}" in csum_output
1822*bee7cf82SDean Marx                    ):
1823*bee7cf82SDean Marx                        self._logger.debug(f"Csum set hw error:\n{csum_output}")
1824*bee7cf82SDean Marx                        raise InteractiveCommandExecutionError(
1825*bee7cf82SDean Marx                            f"Failed to set csum hw {name} mode on port {port_id}"
1826*bee7cf82SDean Marx                        )
1827*bee7cf82SDean Marx                success = False
1828*bee7cf82SDean Marx                if f"{name} checksum offload is hw" in csum_output.lower():
1829*bee7cf82SDean Marx                    success = True
1830*bee7cf82SDean Marx                if not success and verify:
1831*bee7cf82SDean Marx                    self._logger.debug(
1832*bee7cf82SDean Marx                        f"Failed to set csum hw mode on port {port_id}:\n{csum_output}"
1833*bee7cf82SDean Marx                    )
1834*bee7cf82SDean Marx                    raise InteractiveCommandExecutionError(
1835*bee7cf82SDean Marx                        f"""Failed to set csum hw mode on port
1836*bee7cf82SDean Marx                                                           {port_id}:\n{csum_output}"""
1837*bee7cf82SDean Marx                    )
1838*bee7cf82SDean Marx
1839*bee7cf82SDean Marx    @requires_stopped_ports
18409910db35SJeremy Spewock    def set_port_mtu(self, port_id: int, mtu: int, verify: bool = True) -> None:
18419910db35SJeremy Spewock        """Change the MTU of a port using testpmd.
18429910db35SJeremy Spewock
18439910db35SJeremy Spewock        Some PMDs require that the port be stopped before changing the MTU, and it does no harm to
18449910db35SJeremy Spewock        stop the port before configuring in cases where it isn't required, so ports are stopped
18459910db35SJeremy Spewock        prior to changing their MTU.
18469910db35SJeremy Spewock
18479910db35SJeremy Spewock        Args:
18489910db35SJeremy Spewock            port_id: ID of the port to adjust the MTU on.
18499910db35SJeremy Spewock            mtu: Desired value for the MTU to be set to.
18509910db35SJeremy Spewock            verify: If `verify` is :data:`True` then the output will be scanned in an attempt to
18519910db35SJeremy Spewock                verify that the mtu was properly set on the port. Defaults to :data:`True`.
18529910db35SJeremy Spewock
18539910db35SJeremy Spewock        Raises:
18549910db35SJeremy Spewock            InteractiveCommandExecutionError: If `verify` is :data:`True` and the MTU was not
18559910db35SJeremy Spewock                properly updated on the port matching `port_id`.
18569910db35SJeremy Spewock        """
18579910db35SJeremy Spewock        set_mtu_output = self.send_command(f"port config mtu {port_id} {mtu}")
18589910db35SJeremy Spewock        if verify and (f"MTU: {mtu}" not in self.send_command(f"show port info {port_id}")):
18599910db35SJeremy Spewock            self._logger.debug(
18609910db35SJeremy Spewock                f"Failed to set mtu to {mtu} on port {port_id}." f" Output was:\n{set_mtu_output}"
18619910db35SJeremy Spewock            )
18629910db35SJeremy Spewock            raise InteractiveCommandExecutionError(
18639910db35SJeremy Spewock                f"Test pmd failed to update mtu of port {port_id} to {mtu}"
18649910db35SJeremy Spewock            )
18659910db35SJeremy Spewock
18669910db35SJeremy Spewock    def set_port_mtu_all(self, mtu: int, verify: bool = True) -> None:
18679910db35SJeremy Spewock        """Change the MTU of all ports using testpmd.
18689910db35SJeremy Spewock
18699910db35SJeremy Spewock        Runs :meth:`set_port_mtu` for every port that testpmd is aware of.
18709910db35SJeremy Spewock
18719910db35SJeremy Spewock        Args:
18729910db35SJeremy Spewock            mtu: Desired value for the MTU to be set to.
18739910db35SJeremy Spewock            verify: Whether to verify that setting the MTU on each port was successful or not.
18749910db35SJeremy Spewock                Defaults to :data:`True`.
18759910db35SJeremy Spewock
18769910db35SJeremy Spewock        Raises:
18779910db35SJeremy Spewock            InteractiveCommandExecutionError: If `verify` is :data:`True` and the MTU was not
18789910db35SJeremy Spewock                properly updated on at least one port.
18799910db35SJeremy Spewock        """
18809910db35SJeremy Spewock        for port in self.ports:
18819910db35SJeremy Spewock            self.set_port_mtu(port.id, mtu, verify)
18829910db35SJeremy Spewock
1883a91d5f47SJeremy Spewock    @staticmethod
1884a91d5f47SJeremy Spewock    def extract_verbose_output(output: str) -> list[TestPmdVerbosePacket]:
1885a91d5f47SJeremy Spewock        """Extract the verbose information present in given testpmd output.
1886a91d5f47SJeremy Spewock
1887a91d5f47SJeremy Spewock        This method extracts sections of verbose output that begin with the line
1888a91d5f47SJeremy Spewock        "port X/queue Y: sent/received Z packets" and end with the ol_flags of a packet.
1889a91d5f47SJeremy Spewock
1890a91d5f47SJeremy Spewock        Args:
1891a91d5f47SJeremy Spewock            output: Testpmd output that contains verbose information
1892a91d5f47SJeremy Spewock
1893a91d5f47SJeremy Spewock        Returns:
1894a91d5f47SJeremy Spewock            List of parsed packet information gathered from verbose information in `output`.
1895a91d5f47SJeremy Spewock        """
1896a91d5f47SJeremy Spewock        out: list[TestPmdVerbosePacket] = []
1897a91d5f47SJeremy Spewock        prev_header: str = ""
1898a91d5f47SJeremy Spewock        iter = re.finditer(
1899a91d5f47SJeremy Spewock            r"(?P<HEADER>(?:port \d+/queue \d+: (?:received|sent) \d+ packets)?)\s*"
1900a91d5f47SJeremy Spewock            r"(?P<PACKET>src=[\w\s=:-]+?ol_flags: [\w ]+)",
1901a91d5f47SJeremy Spewock            output,
1902a91d5f47SJeremy Spewock        )
1903a91d5f47SJeremy Spewock        for match in iter:
1904a91d5f47SJeremy Spewock            if match.group("HEADER"):
1905a91d5f47SJeremy Spewock                prev_header = match.group("HEADER")
1906a91d5f47SJeremy Spewock            out.append(TestPmdVerbosePacket.parse(f"{prev_header}\n{match.group('PACKET')}"))
1907a91d5f47SJeremy Spewock        return out
1908a91d5f47SJeremy Spewock
19091a7520a1SDean Marx    def set_vlan_filter(self, port: int, enable: bool, verify: bool = True) -> None:
19101a7520a1SDean Marx        """Set vlan filter on.
19111a7520a1SDean Marx
19121a7520a1SDean Marx        Args:
19131a7520a1SDean Marx            port: The port number to enable VLAN filter on.
19141a7520a1SDean Marx            enable: Enable the filter on `port` if :data:`True`, otherwise disable it.
19151a7520a1SDean Marx            verify: If :data:`True`, the output of the command and show port info
19161a7520a1SDean Marx                is scanned to verify that vlan filtering was set successfully.
19171a7520a1SDean Marx
19181a7520a1SDean Marx        Raises:
19191a7520a1SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and the filter
19201a7520a1SDean Marx                fails to update.
19211a7520a1SDean Marx        """
19221a7520a1SDean Marx        filter_cmd_output = self.send_command(f"vlan set filter {'on' if enable else 'off'} {port}")
19231a7520a1SDean Marx        if verify:
19241a7520a1SDean Marx            vlan_settings = self.show_port_info(port_id=port).vlan_offload
19251a7520a1SDean Marx            if enable ^ (vlan_settings is not None and VLANOffloadFlag.FILTER in vlan_settings):
19261a7520a1SDean Marx                self._logger.debug(
19271a7520a1SDean Marx                    f"""Failed to {'enable' if enable else 'disable'}
19281a7520a1SDean Marx                                   filter on port {port}: \n{filter_cmd_output}"""
19291a7520a1SDean Marx                )
19301a7520a1SDean Marx                raise InteractiveCommandExecutionError(
19311a7520a1SDean Marx                    f"""Failed to {'enable' if enable else 'disable'}
19321a7520a1SDean Marx                    filter on port {port}"""
19331a7520a1SDean Marx                )
19341a7520a1SDean Marx
19351a7520a1SDean Marx    def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool = True) -> None:
19361a7520a1SDean Marx        """Add specified vlan tag to the filter list on a port. Requires vlan filter to be on.
19371a7520a1SDean Marx
19381a7520a1SDean Marx        Args:
19391a7520a1SDean Marx            vlan: The vlan tag to add, should be within 1-1005.
19401a7520a1SDean Marx            port: The port number to add the tag on.
19411a7520a1SDean Marx            add: Adds the tag if :data:`True`, otherwise removes the tag.
19421a7520a1SDean Marx            verify: If :data:`True`, the output of the command is scanned to verify that
19431a7520a1SDean Marx                the vlan tag was added to the filter list on the specified port.
19441a7520a1SDean Marx
19451a7520a1SDean Marx        Raises:
19461a7520a1SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and the tag
19471a7520a1SDean Marx                is not added.
19481a7520a1SDean Marx        """
19491a7520a1SDean Marx        rx_cmd_output = self.send_command(f"rx_vlan {'add' if add else 'rm'} {vlan} {port}")
19501a7520a1SDean Marx        if verify:
19511a7520a1SDean Marx            if (
19521a7520a1SDean Marx                "VLAN-filtering disabled" in rx_cmd_output
19531a7520a1SDean Marx                or "Invalid vlan_id" in rx_cmd_output
19541a7520a1SDean Marx                or "Bad arguments" in rx_cmd_output
19551a7520a1SDean Marx            ):
19561a7520a1SDean Marx                self._logger.debug(
19571a7520a1SDean Marx                    f"""Failed to {'add' if add else 'remove'} tag {vlan}
19581a7520a1SDean Marx                    port {port}: \n{rx_cmd_output}"""
19591a7520a1SDean Marx                )
19601a7520a1SDean Marx                raise InteractiveCommandExecutionError(
19611a7520a1SDean Marx                    f"Testpmd failed to {'add' if add else 'remove'} tag {vlan} on port {port}."
19621a7520a1SDean Marx                )
19631a7520a1SDean Marx
19641a7520a1SDean Marx    def set_vlan_strip(self, port: int, enable: bool, verify: bool = True) -> None:
19651a7520a1SDean Marx        """Enable or disable vlan stripping on the specified port.
19661a7520a1SDean Marx
19671a7520a1SDean Marx        Args:
19681a7520a1SDean Marx            port: The port number to use.
19691a7520a1SDean Marx            enable: If :data:`True`, will turn vlan stripping on, otherwise will turn off.
19701a7520a1SDean Marx            verify: If :data:`True`, the output of the command and show port info
19711a7520a1SDean Marx                is scanned to verify that vlan stripping was enabled on the specified port.
19721a7520a1SDean Marx
19731a7520a1SDean Marx        Raises:
19741a7520a1SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and stripping
19751a7520a1SDean Marx                fails to update.
19761a7520a1SDean Marx        """
19771a7520a1SDean Marx        strip_cmd_output = self.send_command(f"vlan set strip {'on' if enable else 'off'} {port}")
19781a7520a1SDean Marx        if verify:
19791a7520a1SDean Marx            vlan_settings = self.show_port_info(port_id=port).vlan_offload
19801a7520a1SDean Marx            if enable ^ (vlan_settings is not None and VLANOffloadFlag.STRIP in vlan_settings):
19811a7520a1SDean Marx                self._logger.debug(
19821a7520a1SDean Marx                    f"""Failed to set strip {'on' if enable else 'off'}
19831a7520a1SDean Marx                    port {port}: \n{strip_cmd_output}"""
19841a7520a1SDean Marx                )
19851a7520a1SDean Marx                raise InteractiveCommandExecutionError(
19861a7520a1SDean Marx                    f"Testpmd failed to set strip {'on' if enable else 'off'} port {port}."
19871a7520a1SDean Marx                )
19881a7520a1SDean Marx
19891a7520a1SDean Marx    @requires_stopped_ports
19901a7520a1SDean Marx    def tx_vlan_set(
19911a7520a1SDean Marx        self, port: int, enable: bool, vlan: int | None = None, verify: bool = True
19921a7520a1SDean Marx    ) -> None:
19931a7520a1SDean Marx        """Set hardware insertion of vlan tags in packets sent on a port.
19941a7520a1SDean Marx
19951a7520a1SDean Marx        Args:
19961a7520a1SDean Marx            port: The port number to use.
19971a7520a1SDean Marx            enable: Sets vlan tag insertion if :data:`True`, and resets if :data:`False`.
19981a7520a1SDean Marx            vlan: The vlan tag to insert if enable is :data:`True`.
19991a7520a1SDean Marx            verify: If :data:`True`, the output of the command is scanned to verify that
20001a7520a1SDean Marx                vlan insertion was enabled on the specified port.
20011a7520a1SDean Marx
20021a7520a1SDean Marx        Raises:
20031a7520a1SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
20041a7520a1SDean Marx                tag is not set.
20051a7520a1SDean Marx        """
20061a7520a1SDean Marx        if enable:
20071a7520a1SDean Marx            tx_vlan_cmd_output = self.send_command(f"tx_vlan set {port} {vlan}")
20081a7520a1SDean Marx            if verify:
20091a7520a1SDean Marx                if (
20101a7520a1SDean Marx                    "Please stop port" in tx_vlan_cmd_output
20111a7520a1SDean Marx                    or "Invalid vlan_id" in tx_vlan_cmd_output
20121a7520a1SDean Marx                    or "Invalid port" in tx_vlan_cmd_output
20131a7520a1SDean Marx                ):
20141a7520a1SDean Marx                    self._logger.debug(
20151a7520a1SDean Marx                        f"Failed to set vlan tag {vlan} on port {port}:\n{tx_vlan_cmd_output}"
20161a7520a1SDean Marx                    )
20171a7520a1SDean Marx                    raise InteractiveCommandExecutionError(
20181a7520a1SDean Marx                        f"Testpmd failed to set vlan insertion tag {vlan} on port {port}."
20191a7520a1SDean Marx                    )
20201a7520a1SDean Marx        else:
20211a7520a1SDean Marx            tx_vlan_cmd_output = self.send_command(f"tx_vlan reset {port}")
20221a7520a1SDean Marx            if verify:
20231a7520a1SDean Marx                if "Please stop port" in tx_vlan_cmd_output or "Invalid port" in tx_vlan_cmd_output:
20241a7520a1SDean Marx                    self._logger.debug(
20251a7520a1SDean Marx                        f"Failed to reset vlan insertion on port {port}: \n{tx_vlan_cmd_output}"
20261a7520a1SDean Marx                    )
20271a7520a1SDean Marx                    raise InteractiveCommandExecutionError(
20281a7520a1SDean Marx                        f"Testpmd failed to reset vlan insertion on port {port}."
20291a7520a1SDean Marx                    )
20301a7520a1SDean Marx
20311a7520a1SDean Marx    def set_promisc(self, port: int, enable: bool, verify: bool = True) -> None:
20321a7520a1SDean Marx        """Enable or disable promiscuous mode for the specified port.
20331a7520a1SDean Marx
20341a7520a1SDean Marx        Args:
20351a7520a1SDean Marx            port: Port number to use.
20361a7520a1SDean Marx            enable: If :data:`True`, turn promiscuous mode on, otherwise turn off.
20371a7520a1SDean Marx            verify: If :data:`True` an additional command will be sent to verify that
20381a7520a1SDean Marx                promiscuous mode is properly set. Defaults to :data:`True`.
20391a7520a1SDean Marx
20401a7520a1SDean Marx        Raises:
20411a7520a1SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and promiscuous mode
20421a7520a1SDean Marx                is not correctly set.
20431a7520a1SDean Marx        """
20441a7520a1SDean Marx        promisc_cmd_output = self.send_command(f"set promisc {port} {'on' if enable else 'off'}")
20451a7520a1SDean Marx        if verify:
20461a7520a1SDean Marx            stats = self.show_port_info(port_id=port)
20471a7520a1SDean Marx            if enable ^ stats.is_promiscuous_mode_enabled:
20481a7520a1SDean Marx                self._logger.debug(
20491a7520a1SDean Marx                    f"Failed to set promiscuous mode on port {port}: \n{promisc_cmd_output}"
20501a7520a1SDean Marx                )
20511a7520a1SDean Marx                raise InteractiveCommandExecutionError(
20521a7520a1SDean Marx                    f"Testpmd failed to set promiscuous mode on port {port}."
20531a7520a1SDean Marx                )
20541a7520a1SDean Marx
20551a7520a1SDean Marx    def set_verbose(self, level: int, verify: bool = True) -> None:
20561a7520a1SDean Marx        """Set debug verbosity level.
20571a7520a1SDean Marx
20581a7520a1SDean Marx        Args:
20591a7520a1SDean Marx            level: 0 - silent except for error
20601a7520a1SDean Marx                1 - fully verbose except for Tx packets
20611a7520a1SDean Marx                2 - fully verbose except for Rx packets
20621a7520a1SDean Marx                >2 - fully verbose
20631a7520a1SDean Marx            verify: If :data:`True` the command output will be scanned to verify that verbose level
20641a7520a1SDean Marx                is properly set. Defaults to :data:`True`.
20651a7520a1SDean Marx
20661a7520a1SDean Marx        Raises:
20671a7520a1SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and verbose level
20681a7520a1SDean Marx            is not correctly set.
20691a7520a1SDean Marx        """
20701a7520a1SDean Marx        verbose_cmd_output = self.send_command(f"set verbose {level}")
20711a7520a1SDean Marx        if verify:
20721a7520a1SDean Marx            if "Change verbose level" not in verbose_cmd_output:
20731a7520a1SDean Marx                self._logger.debug(
20741a7520a1SDean Marx                    f"Failed to set verbose level to {level}: \n{verbose_cmd_output}"
20751a7520a1SDean Marx                )
20761a7520a1SDean Marx                raise InteractiveCommandExecutionError(
20771a7520a1SDean Marx                    f"Testpmd failed to set verbose level to {level}."
20781a7520a1SDean Marx                )
20791a7520a1SDean Marx
20802b648cd4SJeremy Spewock    def _close(self) -> None:
2081369d34b8SJeremy Spewock        """Overrides :meth:`~.interactive_shell.close`."""
20822b648cd4SJeremy Spewock        self.stop()
208392439dc9SJeremy Spewock        self.send_command("quit", "Bye...")
20842b648cd4SJeremy Spewock        return super()._close()
2085c89d0038SJuraj Linkeš
2086c0119400SJuraj Linkeš    """
2087c0119400SJuraj Linkeš    ====== Capability retrieval methods ======
2088c0119400SJuraj Linkeš    """
2089c0119400SJuraj Linkeš
2090a79884f9SJuraj Linkeš    def get_capabilities_rx_offload(
2091a79884f9SJuraj Linkeš        self,
2092a79884f9SJuraj Linkeš        supported_capabilities: MutableSet["NicCapability"],
2093a79884f9SJuraj Linkeš        unsupported_capabilities: MutableSet["NicCapability"],
2094a79884f9SJuraj Linkeš    ) -> None:
2095a79884f9SJuraj Linkeš        """Get all rx offload capabilities and divide them into supported and unsupported.
2096a79884f9SJuraj Linkeš
2097a79884f9SJuraj Linkeš        Args:
2098a79884f9SJuraj Linkeš            supported_capabilities: Supported capabilities will be added to this set.
2099a79884f9SJuraj Linkeš            unsupported_capabilities: Unsupported capabilities will be added to this set.
2100a79884f9SJuraj Linkeš        """
2101a79884f9SJuraj Linkeš        self._logger.debug("Getting rx offload capabilities.")
2102a79884f9SJuraj Linkeš        command = f"show port {self.ports[0].id} rx_offload capabilities"
2103a79884f9SJuraj Linkeš        rx_offload_capabilities_out = self.send_command(command)
2104a79884f9SJuraj Linkeš        rx_offload_capabilities = RxOffloadCapabilities.parse(rx_offload_capabilities_out)
2105a79884f9SJuraj Linkeš        self._update_capabilities_from_flag(
2106a79884f9SJuraj Linkeš            supported_capabilities,
2107a79884f9SJuraj Linkeš            unsupported_capabilities,
2108a79884f9SJuraj Linkeš            RxOffloadCapability,
2109a79884f9SJuraj Linkeš            rx_offload_capabilities.per_port | rx_offload_capabilities.per_queue,
2110a79884f9SJuraj Linkeš        )
2111a79884f9SJuraj Linkeš
2112c986c339SDean Marx    def get_port_queue_info(
2113c986c339SDean Marx        self, port_id: int, queue_id: int, is_rx_queue: bool
2114c986c339SDean Marx    ) -> TestPmdQueueInfo:
2115c986c339SDean Marx        """Returns the current state of the specified queue."""
2116c986c339SDean Marx        command = f"show {'rxq' if is_rx_queue else 'txq'} info {port_id} {queue_id}"
2117c986c339SDean Marx        queue_info = TestPmdQueueInfo.parse(self.send_command(command))
2118c986c339SDean Marx        return queue_info
2119c986c339SDean Marx
2120c986c339SDean Marx    def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: bool) -> None:
2121c986c339SDean Marx        """Setup a given queue on a port.
2122c986c339SDean Marx
2123c986c339SDean Marx        This functionality cannot be verified because the setup action only takes effect when the
2124c986c339SDean Marx        queue is started.
2125c986c339SDean Marx
2126c986c339SDean Marx        Args:
2127c986c339SDean Marx            port_id: ID of the port where the queue resides.
2128c986c339SDean Marx            queue_id: ID of the queue to setup.
2129c986c339SDean Marx            is_rx_queue: Type of queue to setup. If :data:`True` an RX queue will be setup,
2130c986c339SDean Marx                otherwise a TX queue will be setup.
2131c986c339SDean Marx        """
2132c986c339SDean Marx        self.send_command(f"port {port_id} {'rxq' if is_rx_queue else 'txq'} {queue_id} setup")
2133c986c339SDean Marx
2134c986c339SDean Marx    def stop_port_queue(
2135c986c339SDean Marx        self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
2136c986c339SDean Marx    ) -> None:
2137c986c339SDean Marx        """Stops a given queue on a port.
2138c986c339SDean Marx
2139c986c339SDean Marx        Args:
2140c986c339SDean Marx            port_id: ID of the port that the queue belongs to.
2141c986c339SDean Marx            queue_id: ID of the queue to stop.
2142c986c339SDean Marx            is_rx_queue: Type of queue to stop. If :data:`True` an RX queue will be stopped,
2143c986c339SDean Marx                otherwise a TX queue will be stopped.
2144c986c339SDean Marx            verify: If :data:`True` an additional command will be sent to verify the queue stopped.
2145c986c339SDean Marx                Defaults to :data:`True`.
2146c986c339SDean Marx
2147c986c339SDean Marx        Raises:
2148c986c339SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
2149c986c339SDean Marx                stop.
2150c986c339SDean Marx        """
2151c986c339SDean Marx        port_type = "rxq" if is_rx_queue else "txq"
2152c986c339SDean Marx        stop_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} stop")
2153c986c339SDean Marx        if verify:
2154c986c339SDean Marx            queue_started = self.get_port_queue_info(
2155c986c339SDean Marx                port_id, queue_id, is_rx_queue
2156c986c339SDean Marx            ).is_queue_started
2157c986c339SDean Marx            if queue_started:
2158c986c339SDean Marx                self._logger.debug(
2159c986c339SDean Marx                    f"Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_output}"
2160c986c339SDean Marx                )
2161c986c339SDean Marx                raise InteractiveCommandExecutionError(
2162c986c339SDean Marx                    f"Test pmd failed to stop {port_type} {queue_id} on port {port_id}"
2163c986c339SDean Marx                )
2164c986c339SDean Marx
2165c986c339SDean Marx    def start_port_queue(
2166c986c339SDean Marx        self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
2167c986c339SDean Marx    ) -> None:
2168c986c339SDean Marx        """Starts a given queue on a port.
2169c986c339SDean Marx
2170c986c339SDean Marx        First sets up the port queue, then starts it.
2171c986c339SDean Marx
2172c986c339SDean Marx        Args:
2173c986c339SDean Marx            port_id: ID of the port that the queue belongs to.
2174c986c339SDean Marx            queue_id: ID of the queue to start.
2175c986c339SDean Marx            is_rx_queue: Type of queue to start. If :data:`True` an RX queue will be started,
2176c986c339SDean Marx                otherwise a TX queue will be started.
2177c986c339SDean Marx            verify: if :data:`True` an additional command will be sent to verify that the queue was
2178c986c339SDean Marx                started. Defaults to :data:`True`.
2179c986c339SDean Marx
2180c986c339SDean Marx        Raises:
2181c986c339SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
2182c986c339SDean Marx                start.
2183c986c339SDean Marx        """
2184c986c339SDean Marx        port_type = "rxq" if is_rx_queue else "txq"
2185c986c339SDean Marx        self.setup_port_queue(port_id, queue_id, is_rx_queue)
2186c986c339SDean Marx        start_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} start")
2187c986c339SDean Marx        if verify:
2188c986c339SDean Marx            queue_started = self.get_port_queue_info(
2189c986c339SDean Marx                port_id, queue_id, is_rx_queue
2190c986c339SDean Marx            ).is_queue_started
2191c986c339SDean Marx            if not queue_started:
2192c986c339SDean Marx                self._logger.debug(
2193c986c339SDean Marx                    f"Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_output}"
2194c986c339SDean Marx                )
2195c986c339SDean Marx                raise InteractiveCommandExecutionError(
2196c986c339SDean Marx                    f"Test pmd failed to start {port_type} {queue_id} on port {port_id}"
2197c986c339SDean Marx                )
2198c986c339SDean Marx
2199c986c339SDean Marx    def get_queue_ring_size(self, port_id: int, queue_id: int, is_rx_queue: bool) -> int:
2200c986c339SDean Marx        """Returns the current size of the ring on the specified queue."""
2201c986c339SDean Marx        command = f"show {'rxq' if is_rx_queue else 'txq'} info {port_id} {queue_id}"
2202c986c339SDean Marx        queue_info = TestPmdQueueInfo.parse(self.send_command(command))
2203c986c339SDean Marx        return queue_info.ring_size
2204c986c339SDean Marx
2205c986c339SDean Marx    def set_queue_ring_size(
2206c986c339SDean Marx        self,
2207c986c339SDean Marx        port_id: int,
2208c986c339SDean Marx        queue_id: int,
2209c986c339SDean Marx        size: int,
2210c986c339SDean Marx        is_rx_queue: bool,
2211c986c339SDean Marx        verify: bool = True,
2212c986c339SDean Marx    ) -> None:
2213c986c339SDean Marx        """Update the ring size of an Rx/Tx queue on a given port.
2214c986c339SDean Marx
2215c986c339SDean Marx        Queue is setup after setting the ring size so that the queue info reflects this change and
2216c986c339SDean Marx        it can be verified.
2217c986c339SDean Marx
2218c986c339SDean Marx        Args:
2219c986c339SDean Marx            port_id: The port that the queue resides on.
2220c986c339SDean Marx            queue_id: The ID of the queue on the port.
2221c986c339SDean Marx            size: The size to update the ring size to.
2222c986c339SDean Marx            is_rx_queue: Whether to modify an RX or TX queue. If :data:`True` an RX queue will be
2223c986c339SDean Marx                updated, otherwise a TX queue will be updated.
2224c986c339SDean Marx            verify: If :data:`True` an additional command will be sent to check the ring size of
2225c986c339SDean Marx                the queue in an attempt to validate that the size was changes properly.
2226c986c339SDean Marx
2227c986c339SDean Marx        Raises:
2228c986c339SDean Marx            InteractiveCommandExecutionError: If `verify` is :data:`True` and there is a failure
2229c986c339SDean Marx                when updating ring size.
2230c986c339SDean Marx        """
2231c986c339SDean Marx        queue_type = "rxq" if is_rx_queue else "txq"
2232c986c339SDean Marx        self.send_command(f"port config {port_id} {queue_type} {queue_id} ring_size {size}")
2233c986c339SDean Marx        self.setup_port_queue(port_id, queue_id, is_rx_queue)
2234c986c339SDean Marx        if verify:
2235c986c339SDean Marx            curr_ring_size = self.get_queue_ring_size(port_id, queue_id, is_rx_queue)
2236c986c339SDean Marx            if curr_ring_size != size:
2237c986c339SDean Marx                self._logger.debug(
2238c986c339SDean Marx                    f"Failed up update ring size of queue {queue_id} on port {port_id}. Current"
2239c986c339SDean Marx                    f" ring size is {curr_ring_size}."
2240c986c339SDean Marx                )
2241c986c339SDean Marx                raise InteractiveCommandExecutionError(
2242c986c339SDean Marx                    f"Failed to update ring size of queue {queue_id} on port {port_id}"
2243c986c339SDean Marx                )
2244c986c339SDean Marx
2245a79884f9SJuraj Linkeš    def _update_capabilities_from_flag(
2246a79884f9SJuraj Linkeš        self,
2247a79884f9SJuraj Linkeš        supported_capabilities: MutableSet["NicCapability"],
2248a79884f9SJuraj Linkeš        unsupported_capabilities: MutableSet["NicCapability"],
2249a79884f9SJuraj Linkeš        flag_class: type[Flag],
2250a79884f9SJuraj Linkeš        supported_flags: Flag,
2251a79884f9SJuraj Linkeš    ) -> None:
2252a79884f9SJuraj Linkeš        """Divide all flags from `flag_class` into supported and unsupported."""
2253a79884f9SJuraj Linkeš        for flag in flag_class:
2254a79884f9SJuraj Linkeš            if flag in supported_flags:
2255a79884f9SJuraj Linkeš                supported_capabilities.add(NicCapability[str(flag.name)])
2256a79884f9SJuraj Linkeš            else:
2257a79884f9SJuraj Linkeš                unsupported_capabilities.add(NicCapability[str(flag.name)])
2258a79884f9SJuraj Linkeš
2259c0119400SJuraj Linkeš    @requires_started_ports
2260c0119400SJuraj Linkeš    def get_capabilities_rxq_info(
2261c0119400SJuraj Linkeš        self,
2262c0119400SJuraj Linkeš        supported_capabilities: MutableSet["NicCapability"],
2263c0119400SJuraj Linkeš        unsupported_capabilities: MutableSet["NicCapability"],
2264c0119400SJuraj Linkeš    ) -> None:
2265c0119400SJuraj Linkeš        """Get all rxq capabilities and divide them into supported and unsupported.
2266c0119400SJuraj Linkeš
2267c0119400SJuraj Linkeš        Args:
2268c0119400SJuraj Linkeš            supported_capabilities: Supported capabilities will be added to this set.
2269c0119400SJuraj Linkeš            unsupported_capabilities: Unsupported capabilities will be added to this set.
2270c0119400SJuraj Linkeš        """
2271c0119400SJuraj Linkeš        self._logger.debug("Getting rxq capabilities.")
2272c0119400SJuraj Linkeš        command = f"show rxq info {self.ports[0].id} 0"
2273c0119400SJuraj Linkeš        rxq_info = TestPmdRxqInfo.parse(self.send_command(command))
2274c986c339SDean Marx        if rxq_info.scattered_packets:
2275c0119400SJuraj Linkeš            supported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED)
2276c0119400SJuraj Linkeš        else:
2277c0119400SJuraj Linkeš            unsupported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED)
2278c0119400SJuraj Linkeš
2279d64f6ba5SJuraj Linkeš    def get_capabilities_show_port_info(
2280d64f6ba5SJuraj Linkeš        self,
2281d64f6ba5SJuraj Linkeš        supported_capabilities: MutableSet["NicCapability"],
2282d64f6ba5SJuraj Linkeš        unsupported_capabilities: MutableSet["NicCapability"],
2283d64f6ba5SJuraj Linkeš    ) -> None:
2284d64f6ba5SJuraj Linkeš        """Get all capabilities from show port info and divide them into supported and unsupported.
2285d64f6ba5SJuraj Linkeš
2286d64f6ba5SJuraj Linkeš        Args:
2287d64f6ba5SJuraj Linkeš            supported_capabilities: Supported capabilities will be added to this set.
2288d64f6ba5SJuraj Linkeš            unsupported_capabilities: Unsupported capabilities will be added to this set.
2289d64f6ba5SJuraj Linkeš        """
2290d64f6ba5SJuraj Linkeš        self._update_capabilities_from_flag(
2291d64f6ba5SJuraj Linkeš            supported_capabilities,
2292d64f6ba5SJuraj Linkeš            unsupported_capabilities,
2293d64f6ba5SJuraj Linkeš            DeviceCapabilitiesFlag,
2294d64f6ba5SJuraj Linkeš            self.ports[0].device_capabilities,
2295d64f6ba5SJuraj Linkeš        )
2296d64f6ba5SJuraj Linkeš
2297474ce443SNicholas Pratte    def get_capabilities_mcast_filtering(
2298474ce443SNicholas Pratte        self,
2299474ce443SNicholas Pratte        supported_capabilities: MutableSet["NicCapability"],
2300474ce443SNicholas Pratte        unsupported_capabilities: MutableSet["NicCapability"],
2301474ce443SNicholas Pratte    ) -> None:
2302474ce443SNicholas Pratte        """Get multicast filtering capability from mcast_addr add and check for testpmd error code.
2303474ce443SNicholas Pratte
2304474ce443SNicholas Pratte        Args:
2305474ce443SNicholas Pratte            supported_capabilities: Supported capabilities will be added to this set.
2306474ce443SNicholas Pratte            unsupported_capabilities: Unsupported capabilities will be added to this set.
2307474ce443SNicholas Pratte        """
2308474ce443SNicholas Pratte        self._logger.debug("Getting mcast filter capabilities.")
2309474ce443SNicholas Pratte        command = f"mcast_addr add {self.ports[0].id} 01:00:5E:00:00:00"
2310474ce443SNicholas Pratte        output = self.send_command(command)
2311474ce443SNicholas Pratte        if "diag=-95" in output:
2312474ce443SNicholas Pratte            unsupported_capabilities.add(NicCapability.MCAST_FILTERING)
2313474ce443SNicholas Pratte        else:
2314474ce443SNicholas Pratte            supported_capabilities.add(NicCapability.MCAST_FILTERING)
2315474ce443SNicholas Pratte            command = str.replace(command, "add", "remove", 1)
2316474ce443SNicholas Pratte            self.send_command(command)
2317474ce443SNicholas Pratte
2318c89d0038SJuraj Linkeš
2319c89d0038SJuraj Linkešclass NicCapability(NoAliasEnum):
2320c89d0038SJuraj Linkeš    """A mapping between capability names and the associated :class:`TestPmdShell` methods.
2321c89d0038SJuraj Linkeš
2322c89d0038SJuraj Linkeš    The :class:`TestPmdShell` capability checking method executes the command that checks
2323c89d0038SJuraj Linkeš    whether the capability is supported.
2324c89d0038SJuraj Linkeš    A decorator may optionally be added to the method that will add and remove configuration
2325c89d0038SJuraj Linkeš    that's necessary to retrieve the capability support status.
2326c89d0038SJuraj Linkeš    The Enum members may be assigned the method itself or a tuple of
2327c89d0038SJuraj Linkeš    (capability_checking_method, decorator_function).
2328c89d0038SJuraj Linkeš
2329c89d0038SJuraj Linkeš    The signature of each :class:`TestPmdShell` capability checking method must be::
2330c89d0038SJuraj Linkeš
2331c89d0038SJuraj Linkeš        fn(self, supported_capabilities: MutableSet, unsupported_capabilities: MutableSet) -> None
2332c89d0038SJuraj Linkeš
2333c89d0038SJuraj Linkeš    The capability checking method must get whether a capability is supported or not
2334c89d0038SJuraj Linkeš    from a testpmd command. If multiple capabilities can be obtained from a testpmd command,
2335c89d0038SJuraj Linkeš    each should be obtained in the method. These capabilities should then
2336c89d0038SJuraj Linkeš    be added to `supported_capabilities` or `unsupported_capabilities` based on their support.
2337c89d0038SJuraj Linkeš
2338c89d0038SJuraj Linkeš    The two dictionaries are shared across all capability discovery function calls in a given
2339c0119400SJuraj Linkeš    test run so that we don't call the same function multiple times. For example, when we find
2340c0119400SJuraj Linkeš    :attr:`SCATTERED_RX_ENABLED` in :meth:`TestPmdShell.get_capabilities_rxq_info`,
2341c0119400SJuraj Linkeš    we don't go looking for it again if a different test case also needs it.
2342c89d0038SJuraj Linkeš    """
2343c89d0038SJuraj Linkeš
2344c0119400SJuraj Linkeš    #: Scattered packets Rx enabled
2345c0119400SJuraj Linkeš    SCATTERED_RX_ENABLED: TestPmdShellNicCapability = (
2346c0119400SJuraj Linkeš        TestPmdShell.get_capabilities_rxq_info,
2347c0119400SJuraj Linkeš        add_remove_mtu(9000),
2348c0119400SJuraj Linkeš    )
2349a79884f9SJuraj Linkeš    #:
2350a79884f9SJuraj Linkeš    RX_OFFLOAD_VLAN_STRIP: TestPmdShellCapabilityMethod = functools.partial(
2351a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2352a79884f9SJuraj Linkeš    )
2353a79884f9SJuraj Linkeš    #: Device supports L3 checksum offload.
2354a79884f9SJuraj Linkeš    RX_OFFLOAD_IPV4_CKSUM: TestPmdShellCapabilityMethod = functools.partial(
2355a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2356a79884f9SJuraj Linkeš    )
2357a79884f9SJuraj Linkeš    #: Device supports L4 checksum offload.
2358a79884f9SJuraj Linkeš    RX_OFFLOAD_UDP_CKSUM: TestPmdShellCapabilityMethod = functools.partial(
2359a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2360a79884f9SJuraj Linkeš    )
2361a79884f9SJuraj Linkeš    #: Device supports L4 checksum offload.
2362a79884f9SJuraj Linkeš    RX_OFFLOAD_TCP_CKSUM: TestPmdShellCapabilityMethod = functools.partial(
2363a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2364a79884f9SJuraj Linkeš    )
2365a79884f9SJuraj Linkeš    #: Device supports Large Receive Offload.
2366a79884f9SJuraj Linkeš    RX_OFFLOAD_TCP_LRO: TestPmdShellCapabilityMethod = functools.partial(
2367a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2368a79884f9SJuraj Linkeš    )
2369a79884f9SJuraj Linkeš    #: Device supports QinQ (queue in queue) offload.
2370a79884f9SJuraj Linkeš    RX_OFFLOAD_QINQ_STRIP: TestPmdShellCapabilityMethod = functools.partial(
2371a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2372a79884f9SJuraj Linkeš    )
2373a79884f9SJuraj Linkeš    #: Device supports inner packet L3 checksum.
2374a79884f9SJuraj Linkeš    RX_OFFLOAD_OUTER_IPV4_CKSUM: TestPmdShellCapabilityMethod = functools.partial(
2375a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2376a79884f9SJuraj Linkeš    )
2377a79884f9SJuraj Linkeš    #: Device supports MACsec.
2378a79884f9SJuraj Linkeš    RX_OFFLOAD_MACSEC_STRIP: TestPmdShellCapabilityMethod = functools.partial(
2379a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2380a79884f9SJuraj Linkeš    )
2381a79884f9SJuraj Linkeš    #: Device supports filtering of a VLAN Tag identifier.
2382a79884f9SJuraj Linkeš    RX_OFFLOAD_VLAN_FILTER: TestPmdShellCapabilityMethod = functools.partial(
2383a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2384a79884f9SJuraj Linkeš    )
2385a79884f9SJuraj Linkeš    #: Device supports VLAN offload.
2386a79884f9SJuraj Linkeš    RX_OFFLOAD_VLAN_EXTEND: TestPmdShellCapabilityMethod = functools.partial(
2387a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2388a79884f9SJuraj Linkeš    )
2389a79884f9SJuraj Linkeš    #: Device supports receiving segmented mbufs.
2390a79884f9SJuraj Linkeš    RX_OFFLOAD_SCATTER: TestPmdShellCapabilityMethod = functools.partial(
2391a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2392a79884f9SJuraj Linkeš    )
2393a79884f9SJuraj Linkeš    #: Device supports Timestamp.
2394a79884f9SJuraj Linkeš    RX_OFFLOAD_TIMESTAMP: TestPmdShellCapabilityMethod = functools.partial(
2395a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2396a79884f9SJuraj Linkeš    )
2397a79884f9SJuraj Linkeš    #: Device supports crypto processing while packet is received in NIC.
2398a79884f9SJuraj Linkeš    RX_OFFLOAD_SECURITY: TestPmdShellCapabilityMethod = functools.partial(
2399a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2400a79884f9SJuraj Linkeš    )
2401a79884f9SJuraj Linkeš    #: Device supports CRC stripping.
2402a79884f9SJuraj Linkeš    RX_OFFLOAD_KEEP_CRC: TestPmdShellCapabilityMethod = functools.partial(
2403a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2404a79884f9SJuraj Linkeš    )
2405a79884f9SJuraj Linkeš    #: Device supports L4 checksum offload.
2406a79884f9SJuraj Linkeš    RX_OFFLOAD_SCTP_CKSUM: TestPmdShellCapabilityMethod = functools.partial(
2407a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2408a79884f9SJuraj Linkeš    )
2409a79884f9SJuraj Linkeš    #: Device supports inner packet L4 checksum.
2410a79884f9SJuraj Linkeš    RX_OFFLOAD_OUTER_UDP_CKSUM: TestPmdShellCapabilityMethod = functools.partial(
2411a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2412a79884f9SJuraj Linkeš    )
2413a79884f9SJuraj Linkeš    #: Device supports RSS hashing.
2414a79884f9SJuraj Linkeš    RX_OFFLOAD_RSS_HASH: TestPmdShellCapabilityMethod = functools.partial(
2415a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2416a79884f9SJuraj Linkeš    )
2417a79884f9SJuraj Linkeš    #: Device supports scatter Rx packets to segmented mbufs.
2418a79884f9SJuraj Linkeš    RX_OFFLOAD_BUFFER_SPLIT: TestPmdShellCapabilityMethod = functools.partial(
2419a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2420a79884f9SJuraj Linkeš    )
2421a79884f9SJuraj Linkeš    #: Device supports all checksum capabilities.
2422a79884f9SJuraj Linkeš    RX_OFFLOAD_CHECKSUM: TestPmdShellCapabilityMethod = functools.partial(
2423a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2424a79884f9SJuraj Linkeš    )
2425a79884f9SJuraj Linkeš    #: Device supports all VLAN capabilities.
2426a79884f9SJuraj Linkeš    RX_OFFLOAD_VLAN: TestPmdShellCapabilityMethod = functools.partial(
2427a79884f9SJuraj Linkeš        TestPmdShell.get_capabilities_rx_offload
2428a79884f9SJuraj Linkeš    )
2429d64f6ba5SJuraj Linkeš    #: Device supports Rx queue setup after device started.
2430d64f6ba5SJuraj Linkeš    RUNTIME_RX_QUEUE_SETUP: TestPmdShellCapabilityMethod = functools.partial(
2431d64f6ba5SJuraj Linkeš        TestPmdShell.get_capabilities_show_port_info
2432d64f6ba5SJuraj Linkeš    )
2433d64f6ba5SJuraj Linkeš    #: Device supports Tx queue setup after device started.
2434d64f6ba5SJuraj Linkeš    RUNTIME_TX_QUEUE_SETUP: TestPmdShellCapabilityMethod = functools.partial(
2435d64f6ba5SJuraj Linkeš        TestPmdShell.get_capabilities_show_port_info
2436d64f6ba5SJuraj Linkeš    )
2437d64f6ba5SJuraj Linkeš    #: Device supports shared Rx queue among ports within Rx domain and switch domain.
2438d64f6ba5SJuraj Linkeš    RXQ_SHARE: TestPmdShellCapabilityMethod = functools.partial(
2439d64f6ba5SJuraj Linkeš        TestPmdShell.get_capabilities_show_port_info
2440d64f6ba5SJuraj Linkeš    )
2441d64f6ba5SJuraj Linkeš    #: Device supports keeping flow rules across restart.
2442d64f6ba5SJuraj Linkeš    FLOW_RULE_KEEP: TestPmdShellCapabilityMethod = functools.partial(
2443d64f6ba5SJuraj Linkeš        TestPmdShell.get_capabilities_show_port_info
2444d64f6ba5SJuraj Linkeš    )
2445d64f6ba5SJuraj Linkeš    #: Device supports keeping shared flow objects across restart.
2446d64f6ba5SJuraj Linkeš    FLOW_SHARED_OBJECT_KEEP: TestPmdShellCapabilityMethod = functools.partial(
2447d64f6ba5SJuraj Linkeš        TestPmdShell.get_capabilities_show_port_info
2448d64f6ba5SJuraj Linkeš    )
2449474ce443SNicholas Pratte    #: Device supports multicast address filtering.
2450474ce443SNicholas Pratte    MCAST_FILTERING: TestPmdShellCapabilityMethod = functools.partial(
2451474ce443SNicholas Pratte        TestPmdShell.get_capabilities_mcast_filtering
2452474ce443SNicholas Pratte    )
2453c0119400SJuraj Linkeš
2454c89d0038SJuraj Linkeš    def __call__(
2455c89d0038SJuraj Linkeš        self,
2456c89d0038SJuraj Linkeš        testpmd_shell: TestPmdShell,
2457c89d0038SJuraj Linkeš        supported_capabilities: MutableSet[Self],
2458c89d0038SJuraj Linkeš        unsupported_capabilities: MutableSet[Self],
2459c89d0038SJuraj Linkeš    ) -> None:
2460c89d0038SJuraj Linkeš        """Execute the associated capability retrieval function.
2461c89d0038SJuraj Linkeš
2462c89d0038SJuraj Linkeš        Args:
2463c89d0038SJuraj Linkeš            testpmd_shell: :class:`TestPmdShell` object to which the function will be bound to.
2464c89d0038SJuraj Linkeš            supported_capabilities: The dictionary storing the supported capabilities
2465c89d0038SJuraj Linkeš                of a given test run.
2466c89d0038SJuraj Linkeš            unsupported_capabilities: The dictionary storing the unsupported capabilities
2467c89d0038SJuraj Linkeš                of a given test run.
2468c89d0038SJuraj Linkeš        """
2469c89d0038SJuraj Linkeš        self.value(testpmd_shell, supported_capabilities, unsupported_capabilities)
2470