From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id DC8E346F54; Fri, 19 Sep 2025 21:51:35 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 9A2D840647; Fri, 19 Sep 2025 21:51:35 +0200 (CEST) Received: from mail-pf1-f179.google.com (mail-pf1-f179.google.com [209.85.210.179]) by mails.dpdk.org (Postfix) with ESMTP id 835534042F for ; Fri, 19 Sep 2025 21:51:32 +0200 (CEST) Received: by mail-pf1-f179.google.com with SMTP id d2e1a72fcca58-7761a8a1dbcso2360000b3a.1 for ; Fri, 19 Sep 2025 12:51:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iol.unh.edu; s=unh-iol; t=1758311491; x=1758916291; darn=dpdk.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=BUjWOtf4WVodMfjCe/dkjihOvSkMKDSl9LzGMQtHnYU=; b=c+vWog+pGVFwv0mXHqXuWQTrHL+4Bos0EjjKHlPxHDoHArRIxqscHNGcr2f9pzAZXK jyTrVBsX+OdnOOe9nmdbVQR6We8gWUhW0Eq7m0JqjvbpkhVAT0vmWRRdONKLL5GZ9kSz YkZJ26NdLzgMtftqyakfQA0tQ+6krtf1cawFk= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758311491; x=1758916291; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=BUjWOtf4WVodMfjCe/dkjihOvSkMKDSl9LzGMQtHnYU=; b=TAyfwpPinwj0/nuPXH2RH/ntvswFhrYar/8SyLMd6fsj3p/FGduAzeB75e4dnNKj2e PLt4T1EanoMxLgS7WvIU1u4cCdR75cpMmv33V7DqgJ4h5KrGOv7KiLh4P4v7HNikl15M y7oY1FhSnyeVWLMUuRBygJu2FOHDQ4dlHEmvru/Y6f+6NdUMNgaa5sSxMEYa9/w11Q7r L13023vqCtIcvPcOBU54+yf3gZJ7zAARQfACFRSyw1ySMkBd0M4P/pcPS0mulLPhW09W c3/hUx3t0CGWAyeYKEMQ10FCIMchAkQ+I3xEkPs45TR4HU4j3oDp23y7soVksCsTBLOW u6XQ== X-Gm-Message-State: AOJu0YxPKBRkKTLF6V+7yd8nmWi57TNybaAyg6c6wcSiRWlvzsGtSGcV tJwOLTYks34Lj8jonq5vkTN2H9MbzdT7XObDGm1H1raq7jymvdl6f9ng/QZ2ZwC6R1MQdRB8UaB 3pL67S8xaU65OgS6+1jxG0eOmWedY+5KPLiHawm0rXg== X-Gm-Gg: ASbGncu7u+m3LTcAYxNZF5LL8R49DnAL8T2JIr/LFM4W70IwZl33MFPGZkc+o2wDirX jnQkBHmF0GFVgpnIIk87p28YoajwZKz82Oxm9qgQEaI92UMh7HSoKyMdCbz6wxLThuvUW3ykRra ja96k9eWvZ/Dq8UNWa0hAauhJDB5Vryzy+ZAUqrr3kLkQRufBAp1515b/HYqVDnT8KusXdI3Eic e2gI2AiCo8ZZFF/xJ898A== X-Google-Smtp-Source: AGHT+IF9k26XSCd31aUMsDVs5+GwNn91dXX+83ShE8ypBYc7bSanqNrS8WTuMdiqW2OX6nIjn72HE2pLM6t3EjPWvMY= X-Received: by 2002:a05:6a20:939e:b0:249:d3d:a50b with SMTP id adf61e73a8af0-292762e1e5emr6549970637.59.1758311490921; Fri, 19 Sep 2025 12:51:30 -0700 (PDT) MIME-Version: 1.0 References: <20250829174312.2855311-1-paul.szczepanek@arm.com> <20250829174312.2855311-2-paul.szczepanek@arm.com> In-Reply-To: <20250829174312.2855311-2-paul.szczepanek@arm.com> From: Patrick Robb Date: Fri, 19 Sep 2025 15:51:11 -0400 X-Gm-Features: AS18NWC4tSEUMdB5o2gocISbmb6Lp4fXD46RRy69FnXwo5pLel3PgEYi1YVjzlA Message-ID: Subject: Re: [RFC 1/2] dts: move testpmd into API To: Paul Szczepanek Cc: dev@dpdk.org, Ali Alnubani Content-Type: multipart/alternative; boundary="000000000000d23d9c063f2ccb36" X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org --000000000000d23d9c063f2ccb36 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable This series has been applied to next-dts. I am noting here that patchwork CI results shows a checkpatches warning. However, I have run checkpatches on this patch and it's reporting no issues. My DPDK_CHECKPATCH_PATH and codespell dictionary seem fine. So, I am confident enough that there is no issue to apply to next-dts. Ali please let me know if you are concerned. On Fri, Aug 29, 2025 at 1:43=E2=80=AFPM Paul Szczepanek wrote: > Testpmd moved into new API directory. > Capabilities converted into vanilla enum and moved to API. > Removed some ciruclar dependencies and incorrect imports. > > Signed-off-by: Paul Szczepanek > --- > doc/api/dts/api.capabilities.rst | 8 + > doc/api/dts/api.rst | 20 + > ...stpmd_shell.rst =3D> api.testpmd.config.rst} | 4 +- > doc/api/dts/api.testpmd.rst | 15 + > doc/api/dts/api.testpmd.types.rst | 8 + > doc/api/dts/framework.params.rst | 1 - > doc/api/dts/framework.params.testpmd.rst | 8 - > doc/api/dts/framework.remote_session.rst | 1 - > doc/api/dts/index.rst | 1 + > dts/api/__init__.py | 14 + > dts/api/capabilities.py | 180 ++ > dts/api/testpmd/__init__.py | 1294 ++++++++ > .../testpmd.py =3D> api/testpmd/config.py} | 9 +- > dts/api/testpmd/types.py | 1406 ++++++++ > dts/framework/config/__init__.py | 3 +- > dts/framework/params/eal.py | 12 +- > dts/framework/params/types.py | 4 +- > dts/framework/remote_session/__init__.py | 44 - > dts/framework/remote_session/testpmd_shell.py | 2844 ----------------- > dts/framework/testbed_model/capability.py | 144 +- > dts/framework/testbed_model/linux_session.py | 2 +- > dts/framework/testbed_model/os_session.py | 14 +- > dts/framework/testbed_model/topology.py | 30 +- > 23 files changed, 3086 insertions(+), 2980 deletions(-) > create mode 100644 doc/api/dts/api.capabilities.rst > create mode 100644 doc/api/dts/api.rst > rename doc/api/dts/{framework.remote_session.testpmd_shell.rst =3D> > api.testpmd.config.rst} (54%) > create mode 100644 doc/api/dts/api.testpmd.rst > create mode 100644 doc/api/dts/api.testpmd.types.rst > delete mode 100644 doc/api/dts/framework.params.testpmd.rst > create mode 100644 dts/api/__init__.py > create mode 100644 dts/api/capabilities.py > create mode 100644 dts/api/testpmd/__init__.py > rename dts/{framework/params/testpmd.py =3D> api/testpmd/config.py} (98%= ) > create mode 100644 dts/api/testpmd/types.py > delete mode 100644 dts/framework/remote_session/testpmd_shell.py > > diff --git a/doc/api/dts/api.capabilities.rst > b/doc/api/dts/api.capabilities.rst > new file mode 100644 > index 0000000000..311872f61d > --- /dev/null > +++ b/doc/api/dts/api.capabilities.rst > @@ -0,0 +1,8 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + > +capabilities - SUT Capabilities > +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > + > +.. automodule:: api.capabilities > + :members: > + :show-inheritance: > diff --git a/doc/api/dts/api.rst b/doc/api/dts/api.rst > new file mode 100644 > index 0000000000..d3cf1226eb > --- /dev/null > +++ b/doc/api/dts/api.rst > @@ -0,0 +1,20 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + > +api - DTS API > +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > + > +.. automodule:: api > + :members: > + :show-inheritance: > + > +.. toctree:: > + :hidden: > + :maxdepth: 2 > + > + api.testpmd > + > +.. toctree:: > + :hidden: > + :maxdepth: 1 > + > + api.capabilities > \ No newline at end of file > diff --git a/doc/api/dts/framework.remote_session.testpmd_shell.rst > b/doc/api/dts/api.testpmd.config.rst > similarity index 54% > rename from doc/api/dts/framework.remote_session.testpmd_shell.rst > rename to doc/api/dts/api.testpmd.config.rst > index 81ca23337f..d338c07a36 100644 > --- a/doc/api/dts/framework.remote_session.testpmd_shell.rst > +++ b/doc/api/dts/api.testpmd.config.rst > @@ -1,8 +1,8 @@ > .. SPDX-License-Identifier: BSD-3-Clause > > -testpmd\_shell - Testpmd Interactive Remote Shell > +config - Testpmd configuration > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > > -.. automodule:: framework.remote_session.testpmd_shell > +.. automodule:: api.testpmd.config > :members: > :show-inheritance: > diff --git a/doc/api/dts/api.testpmd.rst b/doc/api/dts/api.testpmd.rst > new file mode 100644 > index 0000000000..75a92823f8 > --- /dev/null > +++ b/doc/api/dts/api.testpmd.rst > @@ -0,0 +1,15 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + > +testpmd - Testpmd Interactive Remote Shell > +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > + > +.. automodule:: api.testpmd > + :members: > + :show-inheritance: > + > +.. toctree:: > + :hidden: > + :maxdepth: 1 > + > + api.testpmd.types > + api.testpmd.config > \ No newline at end of file > diff --git a/doc/api/dts/api.testpmd.types.rst > b/doc/api/dts/api.testpmd.types.rst > new file mode 100644 > index 0000000000..75b197aa73 > --- /dev/null > +++ b/doc/api/dts/api.testpmd.types.rst > @@ -0,0 +1,8 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + > +types - Testpmd types > +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > + > +.. automodule:: api.testpmd.types > + :members: > + :show-inheritance: > diff --git a/doc/api/dts/framework.params.rst > b/doc/api/dts/framework.params.rst > index 4e263e2e5c..d8c6af9667 100644 > --- a/doc/api/dts/framework.params.rst > +++ b/doc/api/dts/framework.params.rst > @@ -12,5 +12,4 @@ params - Command Line Parameters Modelling > :maxdepth: 1 > > framework.params.eal > - framework.params.testpmd > framework.params.types > diff --git a/doc/api/dts/framework.params.testpmd.rst > b/doc/api/dts/framework.params.testpmd.rst > deleted file mode 100644 > index 19583b01de..0000000000 > --- a/doc/api/dts/framework.params.testpmd.rst > +++ /dev/null > @@ -1,8 +0,0 @@ > -.. SPDX-License-Identifier: BSD-3-Clause > - > -testpmd - TestPMD Parameters Modelling > -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > - > -.. automodule:: framework.params.testpmd > - :members: > - :show-inheritance: > diff --git a/doc/api/dts/framework.remote_session.rst > b/doc/api/dts/framework.remote_session.rst > index 27c9153e64..b7dbe71412 100644 > --- a/doc/api/dts/framework.remote_session.rst > +++ b/doc/api/dts/framework.remote_session.rst > @@ -18,5 +18,4 @@ remote\_session - Node Connections Package > framework.remote_session.shell_pool > framework.remote_session.dpdk > framework.remote_session.dpdk_shell > - framework.remote_session.testpmd_shell > framework.remote_session.python_shell > diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst > index a11f395e11..c719297c11 100644 > --- a/doc/api/dts/index.rst > +++ b/doc/api/dts/index.rst > @@ -15,6 +15,7 @@ Packages > :maxdepth: 1 > > tests > + api > framework.testbed_model > framework.remote_session > framework.params > diff --git a/dts/api/__init__.py b/dts/api/__init__.py > new file mode 100644 > index 0000000000..26b773bfbb > --- /dev/null > +++ b/dts/api/__init__.py > @@ -0,0 +1,14 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright(c) 2025 Arm Limited > + > +"""DTS API. > + > +This package exposes public API modules for test writers. > + > +All modules in this package are considered stable. Do not use framework > +internal modules in your tests. Any missing functionality should be adde= d > +to the public API. > + > +Private methods and members are prefixed with an underscore and should > not be > +used outside of the framework. > +""" > diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py > new file mode 100644 > index 0000000000..1a79413f6f > --- /dev/null > +++ b/dts/api/capabilities.py > @@ -0,0 +1,180 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright(c) 2024 PANTHEON.tech s.r.o. > +# Copyright(c) 2025 Arm Limited > + > +"""Testbed capabilities. > + > +This module provides a protocol that defines the common attributes of > test cases and suites > +and support for test environment capabilities. > + > +Many test cases are testing features not available on all hardware. > +On the other hand, some test cases or suites may not need the most > complex topology available. > + > +The module allows developers to mark test cases or suites to require > certain hardware capabilities > +or a particular topology. > + > +There are differences between hardware and topology capabilities: > + > + * Hardware capabilities are assumed to not be required when not > specified. > + * However, some topology is always available, so each test case or > suite is assigned > + a default topology if no topology is specified in the decorator. > + > +Examples: > + .. code:: python > + > + from framework.test_suite import TestSuite, func_test > + from framework.testbed_model.capability import LinkTopology, > requires_link_topology > + # The whole test suite (each test case within) doesn't require > any links. > + @requires_link_topology(LinkTopology.NO_LINK) > + @func_test > + class TestHelloWorld(TestSuite): > + def hello_world_single_core(self): > + ... > + > + .. code:: python > + > + from framework.test_suite import TestSuite, func_test > + from framework.testbed_model.capability import NicCapability, > requires_nic_capability > + class TestPmdBufferScatter(TestSuite): > + # only the test case requires the SCATTERED_RX_ENABLED > capability > + # other test cases may not require it > + @requires_nic_capability(NicCapability.SCATTERED_RX_ENABLED) > + @func_test > + def test_scatter_mbuf_2048(self): > +""" > + > +from enum import IntEnum, auto > +from typing import TYPE_CHECKING, Callable > + > +if TYPE_CHECKING: > + from framework.test_suite import TestProtocol > + > + > +class LinkTopology(IntEnum): > + """Supported topology types.""" > + > + #: A topology with no Traffic Generator. > + NO_LINK =3D 0 > + #: A topology with one physical link between the SUT node and the TG > node. > + ONE_LINK =3D auto() > + #: A topology with two physical links between the Sut node and the T= G > node. > + TWO_LINKS =3D auto() > + > + @classmethod > + def default(cls) -> "LinkTopology": > + """The default topology required by test cases if not specified > otherwise.""" > + return cls.TWO_LINKS > + > + > +class NicCapability(IntEnum): > + """DPDK NIC capabilities. > + > + The capabilities are used to mark test cases or suites that require = a > specific > + DPDK NIC capability to run. The capabilities are used by the test > framework to > + determine whether a test case or suite can be run on the current > testbed. > + """ > + > + #: Scattered packets Rx enabled. > + SCATTERED_RX_ENABLED =3D 0 > + #: Device supports VLAN stripping. > + RX_OFFLOAD_VLAN_STRIP =3D auto() > + #: Device supports L3 checksum offload. > + RX_OFFLOAD_IPV4_CKSUM =3D auto() > + #: Device supports L4 checksum offload. > + RX_OFFLOAD_UDP_CKSUM =3D auto() > + #: Device supports L4 checksum offload. > + RX_OFFLOAD_TCP_CKSUM =3D auto() > + #: Device supports Large Receive Offload. > + RX_OFFLOAD_TCP_LRO =3D auto() > + #: Device supports QinQ (queue in queue) offload. > + RX_OFFLOAD_QINQ_STRIP =3D auto() > + #: Device supports inner packet L3 checksum. > + RX_OFFLOAD_OUTER_IPV4_CKSUM =3D auto() > + #: Device supports MACsec. > + RX_OFFLOAD_MACSEC_STRIP =3D auto() > + #: Device supports filtering of a VLAN Tag identifier. > + RX_OFFLOAD_VLAN_FILTER =3D auto() > + #: Device supports VLAN offload. > + RX_OFFLOAD_VLAN_EXTEND =3D auto() > + #: Device supports receiving segmented mbufs. > + RX_OFFLOAD_SCATTER =3D auto() > + #: Device supports Timestamp. > + RX_OFFLOAD_TIMESTAMP =3D auto() > + #: Device supports crypto processing while packet is received in NIC= . > + RX_OFFLOAD_SECURITY =3D auto() > + #: Device supports CRC stripping. > + RX_OFFLOAD_KEEP_CRC =3D auto() > + #: Device supports L4 checksum offload. > + RX_OFFLOAD_SCTP_CKSUM =3D auto() > + #: Device supports inner packet L4 checksum. > + RX_OFFLOAD_OUTER_UDP_CKSUM =3D auto() > + #: Device supports RSS hashing. > + RX_OFFLOAD_RSS_HASH =3D auto() > + #: Device supports scatter Rx packets to segmented mbufs. > + RX_OFFLOAD_BUFFER_SPLIT =3D auto() > + #: Device supports all checksum capabilities. > + RX_OFFLOAD_CHECKSUM =3D auto() > + #: Device supports all VLAN capabilities. > + RX_OFFLOAD_VLAN =3D auto() > + #: Device supports Rx queue setup after device started. > + RUNTIME_RX_QUEUE_SETUP =3D auto() > + #: Device supports Tx queue setup after device started. > + RUNTIME_TX_QUEUE_SETUP =3D auto() > + #: Device supports shared Rx queue among ports within Rx domain and > switch domain. > + RXQ_SHARE =3D auto() > + #: Device supports keeping flow rules across restart. > + FLOW_RULE_KEEP =3D auto() > + #: Device supports keeping shared flow objects across restart. > + FLOW_SHARED_OBJECT_KEEP =3D auto() > + #: Device supports multicast address filtering. > + MCAST_FILTERING =3D auto() > + #: Device supports flow ctrl. > + FLOW_CTRL =3D auto() > + #: Device is running on a physical function. > + PHYSICAL_FUNCTION =3D auto() > + > + > +def requires_link_topology( > + link_topology: LinkTopology, > +) -> Callable[[type["TestProtocol"]], type["TestProtocol"]]: > + """Decorator to set required topology type for a test case or test > suite. > + > + Args: > + link_topology: The topology type the test suite or case requires= . > + > + Returns: > + The decorated test case or test suite. > + """ > + from framework.testbed_model.capability import TopologyCapability > + > + def add_required_topology( > + test_case_or_suite: type["TestProtocol"], > + ) -> type["TestProtocol"]: > + topology_capability =3D TopologyCapability.get_unique(link_topol= ogy) > + topology_capability.set_required(test_case_or_suite) > + return test_case_or_suite > + > + return add_required_topology > + > + > +def requires_nic_capability( > + nic_capability: NicCapability, > +) -> Callable[[type["TestProtocol"]], type["TestProtocol"]]: > + """Decorator to add a single required NIC capability to a test case > or test suite. > + > + Args: > + nic_capability: The NIC capability that is required by the test > case or test suite. > + > + Returns: > + The decorated test case or test suite. > + """ > + from framework.testbed_model.capability import DecoratedNicCapabilit= y > + > + def add_required_capability( > + test_case_or_suite: type["TestProtocol"], > + ) -> type["TestProtocol"]: > + decorated =3D DecoratedNicCapability.get_unique(nic_capability) > + decorated.add_to_required(test_case_or_suite) > + return test_case_or_suite > + > + return add_required_capability > diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py > new file mode 100644 > index 0000000000..49cf3742dd > --- /dev/null > +++ b/dts/api/testpmd/__init__.py > @@ -0,0 +1,1294 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright(c) 2023 University of New Hampshire > +# Copyright(c) 2023 PANTHEON.tech s.r.o. > +# Copyright(c) 2024 Arm Limited > + > +"""Testpmd interactive shell. > + > +Typical usage example in a TestSuite:: > + > + testpmd =3D TestPmd(self.sut_node) > + devices =3D testpmd.get_devices() > + for device in devices: > + print(device) > + testpmd.close() > +""" > + > +import functools > +import re > +import time > +from collections.abc import MutableSet > +from enum import Flag > +from pathlib import PurePath > +from typing import ( > + Any, > + Callable, > + ClassVar, > + Concatenate, > + ParamSpec, > + Tuple, > +) > + > +from typing_extensions import Unpack > + > +from api.capabilities import LinkTopology, NicCapability > +from api.testpmd.config import PortTopology, SimpleForwardingModes, > TestPmdParams > +from api.testpmd.types import ( > + ChecksumOffloadOptions, > + DeviceCapabilitiesFlag, > + FlowRule, > + RxOffloadCapabilities, > + RxOffloadCapability, > + TestPmdDevice, > + TestPmdPort, > + TestPmdPortFlowCtrl, > + TestPmdPortStats, > + TestPmdQueueInfo, > + TestPmdRxqInfo, > + TestPmdVerbosePacket, > + VLANOffloadFlag, > +) > +from framework.context import get_ctx > +from framework.exception import InteractiveCommandExecutionError, > InternalError > +from framework.params.types import TestPmdParamsDict > +from framework.remote_session.dpdk_shell import DPDKShell > +from framework.remote_session.interactive_shell import only_active > +from framework.settings import SETTINGS > + > +P =3D ParamSpec("P") > +TestPmdMethod =3D Callable[Concatenate["TestPmd", P], Any] > + > + > +def _requires_stopped_ports(func: TestPmdMethod) -> TestPmdMethod: > + """Decorator for :class:`TestPmd` commands methods that require > stopped ports. > + > + If the decorated method is called while the ports are started, then > these are stopped before > + continuing. > + > + Args: > + func: The :class:`TestPmd` method to decorate. > + """ > + > + @functools.wraps(func) > + def _wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs): > + if self.ports_started: > + self._logger.debug("Ports need to be stopped to continue.") > + self.stop_all_ports() > + > + return func(self, *args, **kwargs) > + > + return _wrapper > + > + > +def _requires_started_ports(func: TestPmdMethod) -> TestPmdMethod: > + """Decorator for :class:`TestPmd` commands methods that require > started ports. > + > + If the decorated method is called while the ports are stopped, then > these are started before > + continuing. > + > + Args: > + func: The :class:`TestPmd` method to decorate. > + """ > + > + @functools.wraps(func) > + def _wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs): > + if not self.ports_started: > + self._logger.debug("Ports need to be started to continue.") > + self.start_all_ports() > + > + return func(self, *args, **kwargs) > + > + return _wrapper > + > + > +def _add_remove_mtu(mtu: int =3D 1500) -> Callable[[TestPmdMethod], > TestPmdMethod]: > + """Configure MTU to `mtu` on all ports, run the decorated function, > then revert. > + > + Args: > + mtu: The MTU to configure all ports on. > + > + Returns: > + The method decorated with setting and reverting MTU. > + """ > + > + def decorator(func: TestPmdMethod) -> TestPmdMethod: > + @functools.wraps(func) > + def wrapper(self: "TestPmd", *args: P.args, **kwargs: P.kwargs): > + original_mtu =3D self.ports[0].mtu > + self.set_port_mtu_all(mtu=3Dmtu, verify=3DFalse) > + retval =3D func(self, *args, **kwargs) > + self.set_port_mtu_all(original_mtu if original_mtu else 1500= , > verify=3DFalse) > + return retval > + > + return wrapper > + > + return decorator > + > + > +class TestPmd(DPDKShell): > + """Testpmd interactive shell. > + > + The testpmd shell users should never use > + the :meth:`~.interactive_shell.InteractiveShell.send_command` method > directly, but rather > + call specialized methods. If there isn't one that satisfies a need, > it should be added. > + > + Attributes: > + ports_started: Indicates whether the ports are started. > + """ > + > + _app_params: TestPmdParams > + _ports: list[TestPmdPort] | None > + > + #: The testpmd's prompt. > + _default_prompt: ClassVar[str] =3D "testpmd>" > + > + #: This forces the prompt to appear after sending a command. > + _command_extra_chars: ClassVar[str] =3D "\n" > + > + ports_started: bool > + > + def __init__( > + self, > + name: str | None =3D None, > + privileged: bool =3D True, > + **app_params: Unpack[TestPmdParamsDict], > + ) -> None: > + """Overrides :meth:`~.dpdk_shell.DPDKShell.__init__`. Changes > app_params to kwargs.""" > + if "port_topology" not in app_params and get_ctx().topology.type > is LinkTopology.ONE_LINK: > + app_params["port_topology"] =3D PortTopology.loop > + super().__init__(name, privileged, > app_params=3DTestPmdParams(**app_params)) > + self.ports_started =3D not self._app_params.disable_device_start > + self._ports =3D None > + > + @property > + def path(self) -> PurePath: > + """The path to the testpmd executable.""" > + return PurePath("app/dpdk-testpmd") > + > + @property > + def ports(self) -> list[TestPmdPort]: > + """The ports of the instance. > + > + This caches the ports returned by :meth:`show_port_info_all`. > + To force an update of port information, execute > :meth:`show_port_info_all` or > + :meth:`show_port_info`. > + > + Returns: The list of known testpmd ports. > + """ > + if self._ports is None: > + return self.show_port_info_all() > + return self._ports > + > + @_requires_started_ports > + def start(self, verify: bool =3D True) -> None: > + """Start packet forwarding with the current configuration. > + > + Args: > + verify: If :data:`True` , a second start command will be sen= t > in an attempt to verify > + packet forwarding started as expected. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and forwarding fails to > + start or ports fail to come up. > + """ > + self.send_command("start") > + if verify: > + # If forwarding was already started, sending "start" again > should tell us > + start_cmd_output =3D self.send_command("start") > + if "Packet forwarding already started" not in > start_cmd_output: > + self._logger.debug(f"Failed to start packet forwarding: > \n{start_cmd_output}") > + raise InteractiveCommandExecutionError("Testpmd failed t= o > start packet forwarding.") > + > + def stop(self, verify: bool =3D True) -> str: > + """Stop packet forwarding. > + > + Args: > + verify: If :data:`True` , the output of the stop command is > scanned to verify that > + forwarding was stopped successfully or not started. If > neither is found, it is > + considered an error. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the command to stop > + forwarding results in an error. > + > + Returns: > + Output gathered from the stop command and all other precedin= g > logs in the buffer. This > + output is most often used to view forwarding statistics that > are displayed when this > + command is sent as well as any verbose packet information > that hasn't been consumed > + prior to calling this method. > + """ > + stop_cmd_output =3D self.send_command("stop") > + if verify: > + if ( > + "Done." not in stop_cmd_output > + and "Packet forwarding not started" not in stop_cmd_outp= ut > + ): > + self._logger.debug(f"Failed to stop packet forwarding: > \n{stop_cmd_output}") > + raise InteractiveCommandExecutionError("Testpmd failed t= o > stop packet forwarding.") > + return stop_cmd_output > + > + def get_devices(self) -> list[TestPmdDevice]: > + """Get a list of device names that are known to testpmd. > + > + Uses the device info listed in testpmd and then parses the outpu= t. > + > + Returns: > + A list of devices. > + """ > + dev_info: str =3D self.send_command("show device info all") > + dev_list: list[TestPmdDevice] =3D [] > + for line in dev_info.split("\n"): > + if "device name:" in line.lower(): > + dev_list.append(TestPmdDevice(line)) > + return dev_list > + > + def wait_link_status_up(self, port_id: int, timeout=3DSETTINGS.timeo= ut) > -> bool: > + """Wait until the link status on the given port is "up". > + > + Arguments: > + port_id: Port to check the link status on. > + timeout: Time to wait for the link to come up. The default > value for this > + argument may be modified using the :option:`--timeout` > command-line argument > + or the :envvar:`DTS_TIMEOUT` environment variable. > + > + Returns: > + Whether the link came up in time or not. > + """ > + time_to_stop =3D time.time() + timeout > + port_info: str =3D "" > + while time.time() < time_to_stop: > + port_info =3D self.send_command(f"show port info {port_id}") > + if "Link status: up" in port_info: > + break > + time.sleep(0.5) > + else: > + self._logger.error(f"The link for port {port_id} did not com= e > up in the given timeout.") > + return "Link status: up" in port_info > + > + def set_forward_mode(self, mode: SimpleForwardingModes, verify: bool > =3D True): > + """Set packet forwarding mode. > + > + Args: > + mode: The forwarding mode to use. > + verify: If :data:`True` the output of the command will be > scanned in an attempt to > + verify that the forwarding mode was set to `mode` > properly. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the forwarding mode > + fails to update. > + """ > + set_fwd_output =3D self.send_command(f"set fwd {mode.value}") > + if verify: > + if f"Set {mode.value} packet forwarding mode" not in > set_fwd_output: > + self._logger.debug(f"Failed to set fwd mode to > {mode.value}:\n{set_fwd_output}") > + raise InteractiveCommandExecutionError( > + f"Test pmd failed to set fwd mode to {mode.value}" > + ) > + > + def stop_all_ports(self, verify: bool =3D True) -> None: > + """Stops all the ports. > + > + Args: > + verify: If :data:`True`, the output of the command will be > checked for a successful > + execution. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the ports were not > + stopped successfully. > + """ > + self._logger.debug("Stopping all the ports...") > + output =3D self.send_command("port stop all") > + if verify and not output.strip().endswith("Done"): > + raise InteractiveCommandExecutionError("Ports were not > stopped successfully.") > + > + self.ports_started =3D False > + > + def start_all_ports(self, verify: bool =3D True) -> None: > + """Starts all the ports. > + > + Args: > + verify: If :data:`True`, the output of the command will be > checked for a successful > + execution. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the ports were not > + started successfully. > + """ > + self._logger.debug("Starting all the ports...") > + output =3D self.send_command("port start all") > + if verify and not output.strip().endswith("Done"): > + raise InteractiveCommandExecutionError("Ports were not > started successfully.") > + > + self.ports_started =3D True > + > + @_requires_stopped_ports > + def set_ports_queues(self, number_of: int) -> None: > + """Sets the number of queues per port. > + > + Args: > + number_of: The number of RX/TX queues to create per port. > + > + Raises: > + InternalError: If `number_of` is invalid. > + """ > + if number_of < 1: > + raise InternalError("The number of queues must be positive > and non-zero.") > + > + self.send_command(f"port config all rxq {number_of}") > + self.send_command(f"port config all txq {number_of}") > + > + @_requires_stopped_ports > + def close_all_ports(self, verify: bool =3D True) -> None: > + """Close all ports. > + > + Args: > + verify: If :data:`True` the output of the close command will > be scanned in an attempt > + to verify that all ports were stopped successfully. > Defaults to :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and at lease one port > + failed to close. > + """ > + port_close_output =3D self.send_command("port close all") > + if verify: > + num_ports =3D len(self.ports) > + if not all(f"Port {p_id} is closed" in port_close_output for > p_id in range(num_ports)): > + raise InteractiveCommandExecutionError("Ports were not > closed successfully.") > + > + def show_port_info_all(self) -> list[TestPmdPort]: > + """Returns the information of all the ports. > + > + Returns: > + list[TestPmdPort]: A list containing all the ports > information as `TestPmdPort`. > + """ > + output =3D self.send_command("show port info all") > + > + # Sample output of the "all" command looks like: > + # > + # > + # > + # ********************* Infos for port 0 ********************* > + # Key: value > + # > + # ********************* Infos for port 1 ********************* > + # Key: value > + # > + # > + # Takes advantage of the double new line in between ports as end > delimiter. But we need to > + # artificially add a new line at the end to pick up the last > port. Because commands are > + # executed on a pseudo-terminal created by paramiko on the remot= e > node, lines end with CRLF. > + # Therefore we also need to take the carriage return into accoun= t. > + iter =3D re.finditer(r"\*{21}.*?[\r\n]{4}", output + "\r\n", re.= S) > + self._ports =3D [TestPmdPort.parse(block.group(0)) for block in > iter] > + return self._ports > + > + def show_port_info(self, port_id: int) -> TestPmdPort: > + """Returns the given port information. > + > + Args: > + port_id: The port ID to gather information for. > + > + Raises: > + InteractiveCommandExecutionError: If `port_id` is invalid. > + > + Returns: > + TestPmdPort: An instance of `TestPmdPort` containing the > given port's information. > + """ > + output =3D self.send_command(f"show port info {port_id}", > skip_first_line=3DTrue) > + if output.startswith("Invalid port"): > + raise InteractiveCommandExecutionError("invalid port given") > + > + port =3D TestPmdPort.parse(output) > + self._update_port(port) > + return port > + > + def _update_port(self, port: TestPmdPort) -> None: > + if self._ports: > + self._ports =3D [ > + existing_port if port.id !=3D existing_port.id else port > + for existing_port in self._ports > + ] > + > + def set_mac_addr(self, port_id: int, mac_address: str, add: bool, > verify: bool =3D True) -> None: > + """Add or remove a mac address on a given port's Allowlist. > + > + Args: > + port_id: The port ID the mac address is set on. > + mac_address: The mac address to be added to or removed from > the specified port. > + add: If :data:`True`, add the specified mac address. If > :data:`False`, remove specified > + mac address. > + verify: If :data:'True', assert that the 'mac_addr' operatio= n > was successful. If > + :data:'False', run the command and skip this assertion. > + > + Raises: > + InteractiveCommandExecutionError: If the set mac address > operation fails. > + """ > + mac_cmd =3D "add" if add else "remove" > + output =3D self.send_command(f"mac_addr {mac_cmd} {port_id} > {mac_address}") > + if "Bad arguments" in output: > + self._logger.debug("Invalid argument provided to mac_addr") > + raise InteractiveCommandExecutionError("Invalid argument > provided") > + > + if verify: > + if "mac_addr_cmd error:" in output: > + self._logger.debug(f"Failed to {mac_cmd} {mac_address} o= n > port {port_id}") > + raise InteractiveCommandExecutionError( > + f"Failed to {mac_cmd} {mac_address} on port {port_id= } > \n{output}" > + ) > + > + def set_multicast_mac_addr( > + self, port_id: int, multi_addr: str, add: bool, verify: bool =3D > True > + ) -> None: > + """Add or remove multicast mac address to a specified port's > allow list. > + > + Args: > + port_id: The port ID the multicast address is set on. > + multi_addr: The multicast address to be added or removed fro= m > the filter. > + add: If :data:'True', add the specified multicast address to > the port filter. > + If :data:'False', remove the specified multicast address > from the port filter. > + verify: If :data:'True', assert that the 'mcast_addr' > operations was successful. > + If :data:'False', execute the 'mcast_addr' operation and > skip the assertion. > + > + Raises: > + InteractiveCommandExecutionError: If either the 'add' or > 'remove' operations fails. > + """ > + mcast_cmd =3D "add" if add else "remove" > + output =3D self.send_command(f"mcast_addr {mcast_cmd} {port_id} > {multi_addr}") > + if "Bad arguments" in output: > + self._logger.debug("Invalid arguments provided to mcast_addr= ") > + raise InteractiveCommandExecutionError("Invalid argument > provided") > + > + if verify: > + if ( > + "Invalid multicast_addr" in output > + or f"multicast address {'already' if add else 'not'} > filtered by port" in output > + ): > + self._logger.debug(f"Failed to {mcast_cmd} {multi_addr} > on port {port_id}") > + raise InteractiveCommandExecutionError( > + f"Failed to {mcast_cmd} {multi_addr} on port > {port_id} \n{output}" > + ) > + > + def show_port_stats_all(self) -> Tuple[list[TestPmdPortStats], str]: > + """Returns the statistics of all the ports. > + > + Returns: > + Tuple[str, list[TestPmdPortStats]]: A tuple where the first > element is the stats of all > + ports as `TestPmdPortStats` and second is the raw testpmd > output that was collected > + from the sent command. > + """ > + output =3D self.send_command("show port stats all") > + > + # Sample output of the "all" command looks like: > + # > + # ########### NIC statistics for port 0 ########### > + # values... > + # ################################################# > + # > + # ########### NIC statistics for port 1 ########### > + # values... > + # ################################################# > + # > + iter =3D re.finditer(r"(^ #*.+#*$[^#]+)^ #*\r$", output, > re.MULTILINE) > + return ([TestPmdPortStats.parse(block.group(1)) for block in > iter], output) > + > + def show_port_stats(self, port_id: int) -> TestPmdPortStats: > + """Returns the given port statistics. > + > + Args: > + port_id: The port ID to gather information for. > + > + Raises: > + InteractiveCommandExecutionError: If `port_id` is invalid. > + > + Returns: > + TestPmdPortStats: An instance of `TestPmdPortStats` > containing the given port's stats. > + """ > + output =3D self.send_command(f"show port stats {port_id}", > skip_first_line=3DTrue) > + if output.startswith("Invalid port"): > + raise InteractiveCommandExecutionError("invalid port given") > + > + return TestPmdPortStats.parse(output) > + > + def set_multicast_all(self, on: bool, verify: bool =3D True) -> None= : > + """Turns multicast mode on/off for the specified port. > + > + Args: > + on: If :data:`True`, turns multicast mode on, otherwise turn= s > off. > + verify: If :data:`True` an additional command will be sent t= o > verify > + that multicast mode is properly set. Defaults to > :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and multicast > + mode is not properly set. > + """ > + multicast_cmd_output =3D self.send_command(f"set allmulti all {'= on' > if on else 'off'}") > + if verify: > + port_stats =3D self.show_port_info_all() > + if on ^ all(stats.is_allmulticast_mode_enabled for stats in > port_stats): > + self._logger.debug( > + f"Failed to set multicast mode on all ports.: > \n{multicast_cmd_output}" > + ) > + raise InteractiveCommandExecutionError( > + "Testpmd failed to set multicast mode on all ports." > + ) > + > + @_requires_stopped_ports > + def csum_set_hw( > + self, layers: ChecksumOffloadOptions, port_id: int, verify: bool > =3D True > + ) -> None: > + """Enables hardware checksum offloading on the specified layer. > + > + Args: > + layers: The layer/layers that checksum offloading should be > enabled on. > + port_id: The port number to enable checksum offloading on, > should be within 0-32. > + verify: If :data:`True` the output of the command will be > scanned in an attempt to > + verify that checksum offloading was enabled on the port. > + > + Raises: > + InteractiveCommandExecutionError: If checksum offload is not > enabled successfully. > + """ > + for name, offload in ChecksumOffloadOptions.__members__.items(): > + if offload in layers: > + name =3D name.replace("_", "-") > + csum_output =3D self.send_command(f"csum set {name} hw > {port_id}") > + if verify: > + if ( > + "Bad arguments" in csum_output > + or f"Please stop port {port_id} first" in > csum_output > + or f"checksum offload is not supported by port > {port_id}" in csum_output > + ): > + self._logger.debug(f"Csum set hw > error:\n{csum_output}") > + raise InteractiveCommandExecutionError( > + f"Failed to set csum hw {name} mode on port > {port_id}" > + ) > + success =3D False > + if f"{name} checksum offload is hw" in > csum_output.lower(): > + success =3D True > + if not success and verify: > + self._logger.debug( > + f"Failed to set csum hw mode on port > {port_id}:\n{csum_output}" > + ) > + raise InteractiveCommandExecutionError( > + f"""Failed to set csum hw mode on port > + > {port_id}:\n{csum_output}""" > + ) > + > + def flow_create(self, flow_rule: FlowRule, port_id: int) -> int: > + """Creates a flow rule in the testpmd session. > + > + This command is implicitly verified as needed to return the > created flow rule id. > + > + Args: > + flow_rule: :class:`FlowRule` object used for creating testpm= d > flow rule. > + port_id: Integer representing the port to use. > + > + Raises: > + InteractiveCommandExecutionError: If flow rule is invalid. > + > + Returns: > + Id of created flow rule. > + """ > + flow_output =3D self.send_command(f"flow create {port_id} > {flow_rule}") > + match =3D re.search(r"#(\d+)", flow_output) > + if match is not None: > + match_str =3D match.group(1) > + flow_id =3D int(match_str) > + return flow_id > + else: > + self._logger.debug(f"Failed to create flow > rule:\n{flow_output}") > + raise InteractiveCommandExecutionError(f"Failed to create > flow rule:\n{flow_output}") > + > + def flow_validate(self, flow_rule: FlowRule, port_id: int) -> bool: > + """Validates a flow rule in the testpmd session. > + > + Args: > + flow_rule: :class:`FlowRule` object used for validating > testpmd flow rule. > + port_id: Integer representing the port to use. > + > + Returns: > + Boolean representing whether rule is valid or not. > + """ > + flow_output =3D self.send_command(f"flow validate {port_id} > {flow_rule}") > + if "Flow rule validated" in flow_output: > + return True > + return False > + > + def flow_delete(self, flow_id: int, port_id: int, verify: bool =3D > True) -> None: > + """Deletes the specified flow rule from the testpmd session. > + > + Args: > + flow_id: ID of the flow to remove. > + port_id: Integer representing the port to use. > + verify: If :data:`True`, the output of the command is scanne= d > + to ensure the flow rule was deleted successfully. > + > + Raises: > + InteractiveCommandExecutionError: If flow rule is invalid. > + """ > + flow_output =3D self.send_command(f"flow destroy {port_id} rule > {flow_id}") > + if verify: > + if "destroyed" not in flow_output: > + self._logger.debug(f"Failed to delete flow > rule:\n{flow_output}") > + raise InteractiveCommandExecutionError( > + f"Failed to delete flow rule:\n{flow_output}" > + ) > + > + @_requires_started_ports > + @_requires_stopped_ports > + def set_port_mtu(self, port_id: int, mtu: int, verify: bool =3D True= ) > -> None: > + """Change the MTU of a port using testpmd. > + > + Some PMDs require that the port be stopped before changing the > MTU, and it does no harm to > + stop the port before configuring in cases where it isn't > required, so ports are stopped > + prior to changing their MTU. On the other hand, some PMDs requir= e > that the port had already > + been started once since testpmd startup. Therefore, ports are > also started before stopping > + them to ensure this has happened. > + > + Args: > + port_id: ID of the port to adjust the MTU on. > + mtu: Desired value for the MTU to be set to. > + verify: If `verify` is :data:`True` then the output will be > scanned in an attempt to > + verify that the mtu was properly set on the port. > Defaults to :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the MTU was not > + properly updated on the port matching `port_id`. > + """ > + set_mtu_output =3D self.send_command(f"port config mtu {port_id} > {mtu}") > + if verify and (f"MTU: {mtu}" not in self.send_command(f"show por= t > info {port_id}")): > + self._logger.debug( > + f"Failed to set mtu to {mtu} on port {port_id}. Output > was:\n{set_mtu_output}" > + ) > + raise InteractiveCommandExecutionError( > + f"Test pmd failed to update mtu of port {port_id} to > {mtu}" > + ) > + > + def set_port_mtu_all(self, mtu: int, verify: bool =3D True) -> None: > + """Change the MTU of all ports using testpmd. > + > + Runs :meth:`set_port_mtu` for every port that testpmd is aware o= f. > + > + Args: > + mtu: Desired value for the MTU to be set to. > + verify: Whether to verify that setting the MTU on each port > was successful or not. > + Defaults to :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the MTU was not > + properly updated on at least one port. > + """ > + for port in self.ports: > + self.set_port_mtu(port.id, mtu, verify) > + > + @staticmethod > + def extract_verbose_output(output: str) -> list[TestPmdVerbosePacket= ]: > + """Extract the verbose information present in given testpmd > output. > + > + This method extracts sections of verbose output that begin with > the line > + "port X/queue Y: sent/received Z packets" and end with the > ol_flags of a packet. > + > + Args: > + output: Testpmd output that contains verbose information > + > + Returns: > + List of parsed packet information gathered from verbose > information in `output`. > + """ > + out: list[TestPmdVerbosePacket] =3D [] > + prev_header: str =3D "" > + iter =3D re.finditer( > + r"(?P
(?:port \d+/queue \d+: (?:received|sent) \d+ > packets)?)\s*" > + r"(?Psrc=3D[\w\s=3D:-]+?ol_flags: [\w ]+)", > + output, > + ) > + for match in iter: > + if match.group("HEADER"): > + prev_header =3D match.group("HEADER") > + > out.append(TestPmdVerbosePacket.parse(f"{prev_header}\n{match.group('PACK= ET')}")) > + return out > + > + @_requires_stopped_ports > + def set_vlan_filter(self, port: int, enable: bool, verify: bool =3D > True) -> None: > + """Set vlan filter on. > + > + Args: > + port: The port number to enable VLAN filter on. > + enable: Enable the filter on `port` if :data:`True`, > otherwise disable it. > + verify: If :data:`True`, the output of the command and show > port info > + is scanned to verify that vlan filtering was set > successfully. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the filter > + fails to update. > + """ > + filter_cmd_output =3D self.send_command(f"vlan set filter {'on' = if > enable else 'off'} {port}") > + if verify: > + vlan_settings =3D self.show_port_info(port_id=3Dport).vlan_o= ffload > + if enable ^ (vlan_settings is not None and > VLANOffloadFlag.FILTER in vlan_settings): > + self._logger.debug( > + f"""Failed to {"enable" if enable else "disable"} > + filter on port {port}: > \n{filter_cmd_output}""" > + ) > + raise InteractiveCommandExecutionError( > + f"""Failed to {"enable" if enable else "disable"} > + filter on port {port}""" > + ) > + > + def set_mac_address(self, port: int, mac_address: str, verify: bool = =3D > True) -> None: > + """Set port's MAC address. > + > + Args: > + port: The number of the requested port. > + mac_address: The MAC address to set. > + verify: If :data:`True`, the output of the command is scanne= d > to verify that > + the mac address is set in the specified port. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the command > + fails to execute. > + """ > + output =3D self.send_command(f"mac_addr set {port} {mac_address}= ", > skip_first_line=3DTrue) > + if verify: > + if output.strip(): > + self._logger.debug( > + f"Testpmd failed to set MAC address {mac_address} on > port {port}." > + ) > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to set MAC address {mac_address} on > port {port}." > + ) > + > + def set_flow_control( > + self, port: int, flow_ctrl: TestPmdPortFlowCtrl, verify: bool = =3D > True > + ) -> None: > + """Set the given `port`'s flow control. > + > + Args: > + port: The number of the requested port. > + flow_ctrl: The requested flow control parameters. > + verify: If :data:`True`, the output of the command is scanne= d > to verify that > + the flow control in the specified port is set. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the command > + fails to execute. > + """ > + output =3D self.send_command(f"set flow_ctrl {flow_ctrl} {port}"= , > skip_first_line=3DTrue) > + if verify: > + if output.strip(): > + self._logger.debug(f"Testpmd failed to set the > {flow_ctrl} in port {port}.") > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to set the {flow_ctrl} in port > {port}." > + ) > + > + def show_port_flow_info(self, port: int) -> TestPmdPortFlowCtrl | > None: > + """Show port info flow. > + > + Args: > + port: The number of the requested port. > + > + Returns: > + The current port flow control parameters if supported, > otherwise :data:`None`. > + """ > + output =3D self.send_command(f"show port {port} flow_ctrl") > + if "Flow control infos" in output: > + return TestPmdPortFlowCtrl.parse(output) > + return None > + > + @_requires_stopped_ports > + def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool =3D > True) -> None: > + """Add specified vlan tag to the filter list on a port. Requires > vlan filter to be on. > + > + Args: > + vlan: The vlan tag to add, should be within 1-1005. > + port: The port number to add the tag on. > + add: Adds the tag if :data:`True`, otherwise removes the tag= . > + verify: If :data:`True`, the output of the command is scanne= d > to verify that > + the vlan tag was added to the filter list on the > specified port. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the tag > + is not added. > + """ > + rx_cmd_output =3D self.send_command(f"rx_vlan {'add' if add else > 'rm'} {vlan} {port}") > + if verify: > + if ( > + "VLAN-filtering disabled" in rx_cmd_output > + or "Invalid vlan_id" in rx_cmd_output > + or "Bad arguments" in rx_cmd_output > + ): > + self._logger.debug( > + f"""Failed to {"add" if add else "remove"} tag {vlan= } > + port {port}: \n{rx_cmd_output}""" > + ) > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to {'add' if add else 'remove'} tag > {vlan} on port {port}." > + ) > + > + @_requires_stopped_ports > + def set_vlan_strip(self, port: int, enable: bool, verify: bool =3D > True) -> None: > + """Enable or disable vlan stripping on the specified port. > + > + Args: > + port: The port number to use. > + enable: If :data:`True`, will turn vlan stripping on, > otherwise will turn off. > + verify: If :data:`True`, the output of the command and show > port info > + is scanned to verify that vlan stripping was enabled on > the specified port. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and stripping > + fails to update. > + """ > + strip_cmd_output =3D self.send_command(f"vlan set strip {'on' if > enable else 'off'} {port}") > + if verify: > + vlan_settings =3D self.show_port_info(port_id=3Dport).vlan_o= ffload > + if enable ^ (vlan_settings is not None and > VLANOffloadFlag.STRIP in vlan_settings): > + self._logger.debug( > + f"""Failed to set strip {"on" if enable else "off"} > + port {port}: \n{strip_cmd_output}""" > + ) > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to set strip {'on' if enable else > 'off'} port {port}." > + ) > + > + @_requires_stopped_ports > + def tx_vlan_set( > + self, port: int, enable: bool, vlan: int | None =3D None, verify= : > bool =3D True > + ) -> None: > + """Set hardware insertion of vlan tags in packets sent on a port= . > + > + Args: > + port: The port number to use. > + enable: Sets vlan tag insertion if :data:`True`, and resets > if :data:`False`. > + vlan: The vlan tag to insert if enable is :data:`True`. > + verify: If :data:`True`, the output of the command is scanne= d > to verify that > + vlan insertion was enabled on the specified port. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the insertion > + tag is not set. > + """ > + if enable: > + tx_vlan_cmd_output =3D self.send_command(f"tx_vlan set {port= } > {vlan}") > + if verify: > + if ( > + "Please stop port" in tx_vlan_cmd_output > + or "Invalid vlan_id" in tx_vlan_cmd_output > + or "Invalid port" in tx_vlan_cmd_output > + ): > + self._logger.debug( > + f"Failed to set vlan tag {vlan} on port > {port}:\n{tx_vlan_cmd_output}" > + ) > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to set vlan insertion tag {vlan= } > on port {port}." > + ) > + else: > + tx_vlan_cmd_output =3D self.send_command(f"tx_vlan reset > {port}") > + if verify: > + if "Please stop port" in tx_vlan_cmd_output or "Invalid > port" in tx_vlan_cmd_output: > + self._logger.debug( > + f"Failed to reset vlan insertion on port {port}: > \n{tx_vlan_cmd_output}" > + ) > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to reset vlan insertion on port > {port}." > + ) > + > + def set_promisc(self, port: int, enable: bool, verify: bool =3D True= ) > -> None: > + """Enable or disable promiscuous mode for the specified port. > + > + Args: > + port: Port number to use. > + enable: If :data:`True`, turn promiscuous mode on, otherwise > turn off. > + verify: If :data:`True` an additional command will be sent t= o > verify that > + promiscuous mode is properly set. Defaults to > :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and promiscuous mode > + is not correctly set. > + """ > + promisc_cmd_output =3D self.send_command(f"set promisc {port} {'= on' > if enable else 'off'}") > + if verify: > + stats =3D self.show_port_info(port_id=3Dport) > + if enable ^ stats.is_promiscuous_mode_enabled: > + self._logger.debug( > + f"Failed to set promiscuous mode on port {port}: > \n{promisc_cmd_output}" > + ) > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to set promiscuous mode on port > {port}." > + ) > + > + def set_verbose(self, level: int, verify: bool =3D True) -> None: > + """Set debug verbosity level. > + > + Args: > + level: 0 - silent except for error > + 1 - fully verbose except for Tx packets > + 2 - fully verbose except for Rx packets > + >2 - fully verbose > + verify: If :data:`True` the command output will be scanned t= o > verify that verbose level > + is properly set. Defaults to :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and verbose level > + is not correctly set. > + """ > + verbose_cmd_output =3D self.send_command(f"set verbose {level}") > + if verify: > + if "Change verbose level" not in verbose_cmd_output: > + self._logger.debug( > + f"Failed to set verbose level to {level}: > \n{verbose_cmd_output}" > + ) > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to set verbose level to {level}." > + ) > + > + def rx_vxlan(self, vxlan_id: int, port_id: int, enable: bool, verify= : > bool =3D True) -> None: > + """Add or remove vxlan id to/from filter list. > + > + Args: > + vxlan_id: VXLAN ID to add to port filter list. > + port_id: ID of the port to modify VXLAN filter of. > + enable: If :data:`True`, adds specified VXLAN ID, otherwise > removes it. > + verify: If :data:`True`, the output of the command is checke= d > to verify > + the VXLAN ID was successfully added/removed from the por= t. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and VXLAN ID > + is not successfully added or removed. > + """ > + action =3D "add" if enable else "rm" > + vxlan_output =3D self.send_command(f"rx_vxlan_port {action} > {vxlan_id} {port_id}") > + if verify: > + if "udp tunneling add error" in vxlan_output: > + self._logger.debug(f"Failed to set > VXLAN:\n{vxlan_output}") > + raise InteractiveCommandExecutionError(f"Failed to set > VXLAN:\n{vxlan_output}") > + > + def clear_port_stats(self, port_id: int, verify: bool =3D True) -> N= one: > + """Clear statistics of a given port. > + > + Args: > + port_id: ID of the port to clear the statistics on. > + verify: If :data:`True` the output of the command will be > scanned to verify that it was > + successful, otherwise failures will be ignored. Defaults > to :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and testpmd fails to > + clear the statistics of the given port. > + """ > + clear_output =3D self.send_command(f"clear port stats {port_id}"= ) > + if verify and f"NIC statistics for port {port_id} cleared" not i= n > clear_output: > + raise InteractiveCommandExecutionError( > + f"Test pmd failed to set clear forwarding stats on port > {port_id}" > + ) > + > + def clear_port_stats_all(self, verify: bool =3D True) -> None: > + """Clear the statistics of all ports that testpmd is aware of. > + > + Args: > + verify: If :data:`True` the output of the command will be > scanned to verify that all > + ports had their statistics cleared, otherwise failures > will be ignored. Defaults to > + :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and testpmd fails to > + clear the statistics of any of its ports. > + """ > + clear_output =3D self.send_command("clear port stats all") > + if verify: > + if type(self._app_params.port_numa_config) is list: > + for port_id in > range(len(self._app_params.port_numa_config)): > + if f"NIC statistics for port {port_id} cleared" not > in clear_output: > + raise InteractiveCommandExecutionError( > + f"Test pmd failed to set clear forwarding > stats on port {port_id}" > + ) > + > + @only_active > + def close(self) -> None: > + """Overrides :meth:`~.interactive_shell.close`.""" > + self.stop() > + self.send_command("quit", "Bye...") > + return super().close() > + > + """ > + =3D=3D=3D=3D=3D=3D Capability retrieval methods =3D=3D=3D=3D=3D=3D > + """ > + > + def get_capabilities_rx_offload( > + self, > + supported_capabilities: MutableSet["NicCapability"], > + unsupported_capabilities: MutableSet["NicCapability"], > + ) -> None: > + """Get all rx offload capabilities and divide them into supporte= d > and unsupported. > + > + Args: > + supported_capabilities: Supported capabilities will be added > to this set. > + unsupported_capabilities: Unsupported capabilities will be > added to this set. > + """ > + self._logger.debug("Getting rx offload capabilities.") > + command =3D f"show port {self.ports[0].id} rx_offload capabiliti= es" > + rx_offload_capabilities_out =3D self.send_command(command) > + rx_offload_capabilities =3D > RxOffloadCapabilities.parse(rx_offload_capabilities_out) > + self._update_capabilities_from_flag( > + supported_capabilities, > + unsupported_capabilities, > + RxOffloadCapability, > + rx_offload_capabilities.per_port | > rx_offload_capabilities.per_queue, > + ) > + > + def get_port_queue_info( > + self, port_id: int, queue_id: int, is_rx_queue: bool > + ) -> TestPmdQueueInfo: > + """Returns the current state of the specified queue.""" > + command =3D f"show {'rxq' if is_rx_queue else 'txq'} info {port_= id} > {queue_id}" > + queue_info =3D TestPmdQueueInfo.parse(self.send_command(command)= ) > + return queue_info > + > + def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: > bool) -> None: > + """Setup a given queue on a port. > + > + This functionality cannot be verified because the setup action > only takes effect when the > + queue is started. > + > + Args: > + port_id: ID of the port where the queue resides. > + queue_id: ID of the queue to setup. > + is_rx_queue: Type of queue to setup. If :data:`True` an RX > queue will be setup, > + otherwise a TX queue will be setup. > + """ > + self.send_command(f"port {port_id} {'rxq' if is_rx_queue else > 'txq'} {queue_id} setup") > + > + def stop_port_queue( > + self, port_id: int, queue_id: int, is_rx_queue: bool, verify: > bool =3D True > + ) -> None: > + """Stops a given queue on a port. > + > + Args: > + port_id: ID of the port that the queue belongs to. > + queue_id: ID of the queue to stop. > + is_rx_queue: Type of queue to stop. If :data:`True` an RX > queue will be stopped, > + otherwise a TX queue will be stopped. > + verify: If :data:`True` an additional command will be sent t= o > verify the queue stopped. > + Defaults to :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the queue fails to > + stop. > + """ > + port_type =3D "rxq" if is_rx_queue else "txq" > + stop_cmd_output =3D self.send_command(f"port {port_id} {port_typ= e} > {queue_id} stop") > + if verify: > + queue_started =3D self.get_port_queue_info( > + port_id, queue_id, is_rx_queue > + ).is_queue_started > + if queue_started: > + self._logger.debug( > + f"Failed to stop {port_type} {queue_id} on port > {port_id}:\n{stop_cmd_output}" > + ) > + raise InteractiveCommandExecutionError( > + f"Test pmd failed to stop {port_type} {queue_id} on > port {port_id}" > + ) > + > + def start_port_queue( > + self, port_id: int, queue_id: int, is_rx_queue: bool, verify: > bool =3D True > + ) -> None: > + """Starts a given queue on a port. > + > + First sets up the port queue, then starts it. > + > + Args: > + port_id: ID of the port that the queue belongs to. > + queue_id: ID of the queue to start. > + is_rx_queue: Type of queue to start. If :data:`True` an RX > queue will be started, > + otherwise a TX queue will be started. > + verify: if :data:`True` an additional command will be sent t= o > verify that the queue was > + started. Defaults to :data:`True`. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the queue fails to > + start. > + """ > + port_type =3D "rxq" if is_rx_queue else "txq" > + self.setup_port_queue(port_id, queue_id, is_rx_queue) > + start_cmd_output =3D self.send_command(f"port {port_id} {port_ty= pe} > {queue_id} start") > + if verify: > + queue_started =3D self.get_port_queue_info( > + port_id, queue_id, is_rx_queue > + ).is_queue_started > + if not queue_started: > + self._logger.debug( > + f"Failed to start {port_type} {queue_id} on port > {port_id}:\n{start_cmd_output}" > + ) > + raise InteractiveCommandExecutionError( > + f"Test pmd failed to start {port_type} {queue_id} on > port {port_id}" > + ) > + > + def get_queue_ring_size(self, port_id: int, queue_id: int, > is_rx_queue: bool) -> int: > + """Returns the current size of the ring on the specified queue."= "" > + command =3D f"show {'rxq' if is_rx_queue else 'txq'} info {port_= id} > {queue_id}" > + queue_info =3D TestPmdQueueInfo.parse(self.send_command(command)= ) > + return queue_info.ring_size > + > + def set_queue_ring_size( > + self, > + port_id: int, > + queue_id: int, > + size: int, > + is_rx_queue: bool, > + verify: bool =3D True, > + ) -> None: > + """Update the ring size of an Rx/Tx queue on a given port. > + > + Queue is setup after setting the ring size so that the queue inf= o > reflects this change and > + it can be verified. > + > + Args: > + port_id: The port that the queue resides on. > + queue_id: The ID of the queue on the port. > + size: The size to update the ring size to. > + is_rx_queue: Whether to modify an RX or TX queue. If > :data:`True` an RX queue will be > + updated, otherwise a TX queue will be updated. > + verify: If :data:`True` an additional command will be sent t= o > check the ring size of > + the queue in an attempt to validate that the size was > changes properly. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and there is a failure > + when updating ring size. > + """ > + queue_type =3D "rxq" if is_rx_queue else "txq" > + self.send_command(f"port config {port_id} {queue_type} {queue_id= } > ring_size {size}") > + self.setup_port_queue(port_id, queue_id, is_rx_queue) > + if verify: > + curr_ring_size =3D self.get_queue_ring_size(port_id, queue_i= d, > is_rx_queue) > + if curr_ring_size !=3D size: > + self._logger.debug( > + f"Failed up update ring size of queue {queue_id} on > port {port_id}. Current" > + f" ring size is {curr_ring_size}." > + ) > + raise InteractiveCommandExecutionError( > + f"Failed to update ring size of queue {queue_id} on > port {port_id}" > + ) > + > + @_requires_stopped_ports > + def set_queue_deferred_start( > + self, port_id: int, queue_id: int, is_rx_queue: bool, on: bool > + ) -> None: > + """Set the deferred start attribute of the specified queue on/of= f. > + > + Args: > + port_id: The port that the queue resides on. > + queue_id: The ID of the queue on the port. > + is_rx_queue: Whether to modify an RX or TX queue. If > :data:`True` an RX queue will be > + updated, otherwise a TX queue will be updated. > + on: Whether to set deferred start mode on or off. If > :data:`True` deferred start will > + be turned on, otherwise it will be turned off. > + """ > + queue_type =3D "rxq" if is_rx_queue else "txq" > + action =3D "on" if on else "off" > + self.send_command(f"port {port_id} {queue_type} {queue_id} > deferred_start {action}") > + > + def _update_capabilities_from_flag( > + self, > + supported_capabilities: MutableSet["NicCapability"], > + unsupported_capabilities: MutableSet["NicCapability"], > + flag_class: type[Flag], > + supported_flags: Flag, > + ) -> None: > + """Divide all flags from `flag_class` into supported and > unsupported.""" > + for flag in flag_class: > + if flag in supported_flags: > + supported_capabilities.add(NicCapability[str(flag.name)]= ) > + else: > + unsupported_capabilities.add(NicCapability[str(flag.name > )]) > + > + @_requires_started_ports > + def get_capabilities_rxq_info( > + self, > + supported_capabilities: MutableSet["NicCapability"], > + unsupported_capabilities: MutableSet["NicCapability"], > + ) -> None: > + """Get all rxq capabilities and divide them into supported and > unsupported. > + > + Args: > + supported_capabilities: Supported capabilities will be added > to this set. > + unsupported_capabilities: Unsupported capabilities will be > added to this set. > + """ > + self._logger.debug("Getting rxq capabilities.") > + command =3D f"show rxq info {self.ports[0].id} 0" > + rxq_info =3D TestPmdRxqInfo.parse(self.send_command(command)) > + if rxq_info.scattered_packets: > + supported_capabilities.add(NicCapability.SCATTERED_RX_ENABLE= D) > + else: > + > unsupported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED) > + > + def get_capabilities_show_port_info( > + self, > + supported_capabilities: MutableSet["NicCapability"], > + unsupported_capabilities: MutableSet["NicCapability"], > + ) -> None: > + """Get all capabilities from show port info and divide them into > supported and unsupported. > + > + Args: > + supported_capabilities: Supported capabilities will be added > to this set. > + unsupported_capabilities: Unsupported capabilities will be > added to this set. > + """ > + self._update_capabilities_from_flag( > + supported_capabilities, > + unsupported_capabilities, > + DeviceCapabilitiesFlag, > + self.ports[0].device_capabilities, > + ) > + > + def get_capabilities_mcast_filtering( > + self, > + supported_capabilities: MutableSet["NicCapability"], > + unsupported_capabilities: MutableSet["NicCapability"], > + ) -> None: > + """Get multicast filtering capability from mcast_addr add and > check for testpmd error code. > + > + Args: > + supported_capabilities: Supported capabilities will be added > to this set. > + unsupported_capabilities: Unsupported capabilities will be > added to this set. > + """ > + self._logger.debug("Getting mcast filter capabilities.") > + command =3D f"mcast_addr add {self.ports[0].id} 01:00:5E:00:00:0= 0" > + output =3D self.send_command(command) > + if "diag=3D-95" in output: > + unsupported_capabilities.add(NicCapability.MCAST_FILTERING) > + else: > + supported_capabilities.add(NicCapability.MCAST_FILTERING) > + command =3D str.replace(command, "add", "remove", 1) > + self.send_command(command) > + > + def get_capabilities_flow_ctrl( > + self, > + supported_capabilities: MutableSet["NicCapability"], > + unsupported_capabilities: MutableSet["NicCapability"], > + ) -> None: > + """Get flow control capability and check for testpmd failure. > + > + Args: > + supported_capabilities: Supported capabilities will be added > to this set. > + unsupported_capabilities: Unsupported capabilities will be > added to this set. > + """ > + self._logger.debug("Getting flow ctrl capabilities.") > + command =3D f"show port {self.ports[0].id} flow_ctrl" > + output =3D self.send_command(command) > + if "Flow control infos" in output: > + supported_capabilities.add(NicCapability.FLOW_CTRL) > + else: > + unsupported_capabilities.add(NicCapability.FLOW_CTRL) > + > + def get_capabilities_physical_function( > + self, > + supported_capabilities: MutableSet["NicCapability"], > + unsupported_capabilities: MutableSet["NicCapability"], > + ) -> None: > + """Store capability representing a physical function test run. > + > + Args: > + supported_capabilities: Supported capabilities will be added > to this set. > + unsupported_capabilities: Unsupported capabilities will be > added to this set. > + """ > + ctx =3D get_ctx() > + if ctx.topology.vf_ports =3D=3D []: > + supported_capabilities.add(NicCapability.PHYSICAL_FUNCTION) > + else: > + unsupported_capabilities.add(NicCapability.PHYSICAL_FUNCTION= ) > diff --git a/dts/framework/params/testpmd.py b/dts/api/testpmd/config.py > similarity index 98% > rename from dts/framework/params/testpmd.py > rename to dts/api/testpmd/config.py > index 1913bd0fa2..e71a3e1ef0 100644 > --- a/dts/framework/params/testpmd.py > +++ b/dts/api/testpmd/config.py > @@ -1,7 +1,12 @@ > # SPDX-License-Identifier: BSD-3-Clause > # Copyright(c) 2024 Arm Limited > > -"""Module containing all the TestPmd-related parameter classes.""" > +"""Module containing all classes needed to configure :class:`TestPmd`. > + > +This module defines the :class:`TestPmdParams` class which is used to > configure the > +TestPmd shell. It also includes various data classes and enums that are > used > +to represent different configurations and settings. > +""" > > from dataclasses import dataclass, field > from enum import EnumMeta, Flag, auto, unique > @@ -146,7 +151,7 @@ class RSSSetting(EnumMeta): > > > class SimpleForwardingModes(StrEnum): > - r"""The supported packet forwarding modes for > :class:`~TestPmdShell`\s.""" > + r"""The supported packet forwarding modes for :class:`~TestPmd`\s.""= " > > #: > io =3D auto() > diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py > new file mode 100644 > index 0000000000..553438c904 > --- /dev/null > +++ b/dts/api/testpmd/types.py > @@ -0,0 +1,1406 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright(c) 2023 University of New Hampshire > +# Copyright(c) 2023 PANTHEON.tech s.r.o. > +# Copyright(c) 2024 Arm Limited > + > +"""TestPmd types module. > + > +Exposes types used in the TestPmd API. > +""" > + > +import re > +from dataclasses import dataclass, field > +from enum import Flag, auto > +from typing import Literal > + > +from typing_extensions import Self > + > +from framework.parser import ParserFn, TextParser > +from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum > + > + > +class TestPmdDevice: > + """The data of a device that testpmd can recognize. > + > + Attributes: > + pci_address: The PCI address of the device. > + """ > + > + pci_address: str > + > + def __init__(self, pci_address_line: str): > + """Initialize the device from the testpmd output line string. > + > + Args: > + pci_address_line: A line of testpmd output that contains a > device. > + """ > + self.pci_address =3D pci_address_line.strip().split(": ")[1].str= ip() > + > + def __str__(self) -> str: > + """The PCI address captures what the device is.""" > + return self.pci_address > + > + > +class VLANOffloadFlag(Flag): > + """Flag representing the VLAN offload settings of a NIC port.""" > + > + #: > + STRIP =3D auto() > + #: > + FILTER =3D auto() > + #: > + EXTEND =3D auto() > + #: > + QINQ_STRIP =3D auto() > + > + @classmethod > + def from_str_dict(cls, d): > + """Makes an instance from a dict containing the flag member name= s > with an "on" value. > + > + Args: > + d: A dictionary containing the flag members as keys and any > string value. > + > + Returns: > + A new instance of the flag. > + """ > + flag =3D cls(0) > + for name in cls.__members__: > + if d.get(name) =3D=3D "on": > + flag |=3D cls[name] > + return flag > + > + @classmethod > + def make_parser(cls) -> ParserFn: > + """Makes a parser function. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > + parser function that makes an instance of this flag from > text. > + """ > + return TextParser.wrap( > + TextParser.find( > + r"VLAN offload:\s+" > + r"strip (?Pon|off), " > + r"filter (?Pon|off), " > + r"extend (?Pon|off), " > + r"qinq strip (?Pon|off)", > + re.MULTILINE, > + named=3DTrue, > + ), > + cls.from_str_dict, > + ) > + > + > +class ChecksumOffloadOptions(Flag): > + """Flag representing checksum hardware offload layer options.""" > + > + #: > + ip =3D auto() > + #: > + udp =3D auto() > + #: > + tcp =3D auto() > + #: > + sctp =3D auto() > + #: > + outer_ip =3D auto() > + #: > + outer_udp =3D auto() > + > + > +class RSSOffloadTypesFlag(Flag): > + """Flag representing the RSS offload flow types supported by the NIC > port.""" > + > + #: > + ipv4 =3D auto() > + #: > + ipv4_frag =3D auto() > + #: > + ipv4_tcp =3D auto() > + #: > + ipv4_udp =3D auto() > + #: > + ipv4_sctp =3D auto() > + #: > + ipv4_other =3D auto() > + #: > + ipv6 =3D auto() > + #: > + ipv6_frag =3D auto() > + #: > + ipv6_tcp =3D auto() > + #: > + ipv6_udp =3D auto() > + #: > + ipv6_sctp =3D auto() > + #: > + ipv6_other =3D auto() > + #: > + l2_payload =3D auto() > + #: > + ipv6_ex =3D auto() > + #: > + ipv6_tcp_ex =3D auto() > + #: > + ipv6_udp_ex =3D auto() > + #: > + port =3D auto() > + #: > + vxlan =3D auto() > + #: > + geneve =3D auto() > + #: > + nvgre =3D auto() > + #: > + user_defined_22 =3D auto() > + #: > + gtpu =3D auto() > + #: > + eth =3D auto() > + #: > + s_vlan =3D auto() > + #: > + c_vlan =3D auto() > + #: > + esp =3D auto() > + #: > + ah =3D auto() > + #: > + l2tpv3 =3D auto() > + #: > + pfcp =3D auto() > + #: > + pppoe =3D auto() > + #: > + ecpri =3D auto() > + #: > + mpls =3D auto() > + #: > + ipv4_chksum =3D auto() > + #: > + l4_chksum =3D auto() > + #: > + l2tpv2 =3D auto() > + #: > + ipv6_flow_label =3D auto() > + #: > + user_defined_38 =3D auto() > + #: > + user_defined_39 =3D auto() > + #: > + user_defined_40 =3D auto() > + #: > + user_defined_41 =3D auto() > + #: > + user_defined_42 =3D auto() > + #: > + user_defined_43 =3D auto() > + #: > + user_defined_44 =3D auto() > + #: > + user_defined_45 =3D auto() > + #: > + user_defined_46 =3D auto() > + #: > + user_defined_47 =3D auto() > + #: > + user_defined_48 =3D auto() > + #: > + user_defined_49 =3D auto() > + #: > + user_defined_50 =3D auto() > + #: > + user_defined_51 =3D auto() > + #: > + l3_pre96 =3D auto() > + #: > + l3_pre64 =3D auto() > + #: > + l3_pre56 =3D auto() > + #: > + l3_pre48 =3D auto() > + #: > + l3_pre40 =3D auto() > + #: > + l3_pre32 =3D auto() > + #: > + l2_dst_only =3D auto() > + #: > + l2_src_only =3D auto() > + #: > + l4_dst_only =3D auto() > + #: > + l4_src_only =3D auto() > + #: > + l3_dst_only =3D auto() > + #: > + l3_src_only =3D auto() > + > + #: > + ip =3D ipv4 | ipv4_frag | ipv4_other | ipv6 | ipv6_frag | ipv6_other= | > ipv6_ex > + #: > + udp =3D ipv4_udp | ipv6_udp | ipv6_udp_ex > + #: > + tcp =3D ipv4_tcp | ipv6_tcp | ipv6_tcp_ex > + #: > + sctp =3D ipv4_sctp | ipv6_sctp > + #: > + tunnel =3D vxlan | geneve | nvgre > + #: > + vlan =3D s_vlan | c_vlan > + #: > + all =3D ( > + eth > + | vlan > + | ip > + | tcp > + | udp > + | sctp > + | l2_payload > + | l2tpv3 > + | esp > + | ah > + | pfcp > + | gtpu > + | ecpri > + | mpls > + | l2tpv2 > + ) > + > + @classmethod > + def from_list_string(cls, names: str) -> Self: > + """Makes a flag from a whitespace-separated list of names. > + > + Args: > + names: a whitespace-separated list containing the members of > this flag. > + > + Returns: > + An instance of this flag. > + """ > + flag =3D cls(0) > + for name in names.split(): > + flag |=3D cls.from_str(name) > + return flag > + > + @classmethod > + def from_str(cls, name: str) -> Self: > + """Makes a flag matching the supplied name. > + > + Args: > + name: a valid member of this flag in text > + Returns: > + An instance of this flag. > + """ > + member_name =3D name.strip().replace("-", "_") > + return cls[member_name] > + > + @classmethod > + def make_parser(cls) -> ParserFn: > + """Makes a parser function. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > + parser function that makes an instance of this flag from > text. > + """ > + return TextParser.wrap( > + TextParser.find(r"Supported RSS offload flow > types:((?:\r?\n? \S+)+)", re.MULTILINE), > + RSSOffloadTypesFlag.from_list_string, > + ) > + > + > +class DeviceCapabilitiesFlag(Flag): > + """Flag representing the device capabilities.""" > + > + #: Device supports Rx queue setup after device started. > + RUNTIME_RX_QUEUE_SETUP =3D auto() > + #: Device supports Tx queue setup after device started. > + RUNTIME_TX_QUEUE_SETUP =3D auto() > + #: Device supports shared Rx queue among ports within Rx domain and > switch domain. > + RXQ_SHARE =3D auto() > + #: Device supports keeping flow rules across restart. > + FLOW_RULE_KEEP =3D auto() > + #: Device supports keeping shared flow objects across restart. > + FLOW_SHARED_OBJECT_KEEP =3D auto() > + > + @classmethod > + def make_parser(cls) -> ParserFn: > + """Makes a parser function. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > + parser function that makes an instance of this flag from > text. > + """ > + return TextParser.wrap( > + TextParser.find_int(r"Device capabilities: (0x[A-Fa-f\d]+)")= , > + cls, > + ) > + > + > +class DeviceErrorHandlingMode(StrEnum): > + """Enum representing the device error handling mode.""" > + > + #: > + none =3D auto() > + #: > + passive =3D auto() > + #: > + proactive =3D auto() > + #: > + unknown =3D auto() > + > + @classmethod > + def make_parser(cls) -> ParserFn: > + """Makes a parser function. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > + parser function that makes an instance of this enum from > text. > + """ > + return TextParser.wrap(TextParser.find(r"Device error handling > mode: (\w+)"), cls) > + > + > +class RxQueueState(StrEnum): > + """RX queue states. > + > + References: > + DPDK lib: ``lib/ethdev/rte_ethdev.h`` > + testpmd display function: > ``app/test-pmd/config.c:get_queue_state_name()`` > + """ > + > + #: > + stopped =3D auto() > + #: > + started =3D auto() > + #: > + hairpin =3D auto() > + #: > + unknown =3D auto() > + > + @classmethod > + def make_parser(cls) -> ParserFn: > + """Makes a parser function. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > + parser function that makes an instance of this enum from > text. > + """ > + return TextParser.wrap(TextParser.find(r"Rx queue state: > ([^\r\n]+)"), cls) > + > + > +@dataclass > +class TestPmdQueueInfo(TextParser): > + """Dataclass representation of the common parts of the testpmd `show > rxq/txq info` commands.""" > + > + #: > + prefetch_threshold: int =3D > field(metadata=3DTextParser.find_int(r"prefetch threshold: (\d+)")) > + #: > + host_threshold: int =3D field(metadata=3DTextParser.find_int(r"host > threshold: (\d+)")) > + #: > + writeback_threshold: int =3D > field(metadata=3DTextParser.find_int(r"writeback threshold: (\d+)")) > + #: > + free_threshold: int =3D field(metadata=3DTextParser.find_int(r"free > threshold: (\d+)")) > + #: > + deferred_start: bool =3D field(metadata=3DTextParser.find("deferred > start: on")) > + #: The number of RXD/TXDs is just the ring size of the queue. > + ring_size: int =3D field(metadata=3DTextParser.find_int(r"Number of > (?:RXDs|TXDs): (\d+)")) > + #: > + is_queue_started: bool =3D field(metadata=3DTextParser.find("queue s= tate: > started")) > + #: > + burst_mode: str | None =3D field( > + default=3DNone, metadata=3DTextParser.find(r"Burst mode: ([^\r\n= ]+)") > + ) > + > + > +@dataclass > +class TestPmdTxqInfo(TestPmdQueueInfo): > + """Representation of testpmd's ``show txq info `= ` > command. > + > + References: > + testpmd command function: > ``app/test-pmd/cmdline.c:cmd_showqueue()`` > + testpmd display function: > ``app/test-pmd/config.c:rx_queue_infos_display()`` > + """ > + > + #: Ring size threshold > + rs_threshold: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"TX RS threshold= : > (\d+)\b") > + ) > + > + > +@dataclass > +class TestPmdRxqInfo(TestPmdQueueInfo): > + """Representation of testpmd's ``show rxq info `= ` > command. > + > + References: > + testpmd command function: > ``app/test-pmd/cmdline.c:cmd_showqueue()`` > + testpmd display function: > ``app/test-pmd/config.c:rx_queue_infos_display()`` > + """ > + > + #: Mempool used by that queue > + mempool: str | None =3D field(default=3DNone, > metadata=3DTextParser.find(r"Mempool: ([^\r\n]+)")) > + #: Drop packets if no descriptors are available > + drop_packets: bool | None =3D field( > + default=3DNone, metadata=3DTextParser.find(r"RX drop packets: on= ") > + ) > + #: Scattered packets Rx enabled > + scattered_packets: bool | None =3D field( > + default=3DNone, metadata=3DTextParser.find(r"RX scattered packet= s: > on") > + ) > + #: The state of the queue > + queue_state: str | None =3D field(default=3DNone, > metadata=3DRxQueueState.make_parser()) > + > + > +@dataclass > +class TestPmdPort(TextParser): > + """Dataclass representing the result of testpmd's ``show port info`` > command.""" > + > + @staticmethod > + def _make_device_private_info_parser() -> ParserFn: > + """Device private information parser. > + > + Ensures that we are not parsing invalid device private info > output. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a parser > + function that parses the device private info from the > TestPmd port info output. > + """ > + > + def _validate(info: str): > + info =3D info.strip() > + if ( > + info =3D=3D "none" > + or info.startswith("Invalid file") > + or info.startswith("Failed to dump") > + ): > + return None > + return info > + > + return TextParser.wrap(TextParser.find(r"Device private > info:\s+([\s\S]+)"), _validate) > + > + #: > + id: int =3D field(metadata=3DTextParser.find_int(r"Infos for port > (\d+)\b")) > + #: > + device_name: str =3D field(metadata=3DTextParser.find(r"Device name: > ([^\r\n]+)")) > + #: > + driver_name: str =3D field(metadata=3DTextParser.find(r"Driver name: > ([^\r\n]+)")) > + #: > + socket_id: int =3D field(metadata=3DTextParser.find_int(r"Connect to > socket: (\d+)")) > + #: > + is_link_up: bool =3D field(metadata=3DTextParser.find("Link status: = up")) > + #: > + link_speed: str =3D field(metadata=3DTextParser.find(r"Link speed: > ([^\r\n]+)")) > + #: > + is_link_full_duplex: bool =3D field(metadata=3DTextParser.find("Link > duplex: full-duplex")) > + #: > + is_link_autonegotiated: bool =3D > field(metadata=3DTextParser.find("Autoneg status: On")) > + #: > + is_promiscuous_mode_enabled: bool =3D > field(metadata=3DTextParser.find("Promiscuous mode: enabled")) > + #: > + is_allmulticast_mode_enabled: bool =3D field( > + metadata=3DTextParser.find("Allmulticast mode: enabled") > + ) > + #: Maximum number of MAC addresses > + max_mac_addresses_num: int =3D field( > + metadata=3DTextParser.find_int(r"Maximum number of MAC addresses= : > (\d+)") > + ) > + #: Maximum configurable length of RX packet > + max_hash_mac_addresses_num: int =3D field( > + metadata=3DTextParser.find_int(r"Maximum number of MAC addresses= of > hash filtering: (\d+)") > + ) > + #: Minimum size of RX buffer > + min_rx_bufsize: int =3D field(metadata=3DTextParser.find_int(r"Minim= um > size of RX buffer: (\d+)")) > + #: Maximum configurable length of RX packet > + max_rx_packet_length: int =3D field( > + metadata=3DTextParser.find_int(r"Maximum configurable length of = RX > packet: (\d+)") > + ) > + #: Maximum configurable size of LRO aggregated packet > + max_lro_packet_size: int =3D field( > + metadata=3DTextParser.find_int(r"Maximum configurable size of LR= O > aggregated packet: (\d+)") > + ) > + > + #: Current number of RX queues > + rx_queues_num: int =3D field(metadata=3DTextParser.find_int(r"Curren= t > number of RX queues: (\d+)")) > + #: Max possible RX queues > + max_rx_queues_num: int =3D field(metadata=3DTextParser.find_int(r"Ma= x > possible RX queues: (\d+)")) > + #: Max possible number of RXDs per queue > + max_queue_rxd_num: int =3D field( > + metadata=3DTextParser.find_int(r"Max possible number of RXDs per > queue: (\d+)") > + ) > + #: Min possible number of RXDs per queue > + min_queue_rxd_num: int =3D field( > + metadata=3DTextParser.find_int(r"Min possible number of RXDs per > queue: (\d+)") > + ) > + #: RXDs number alignment > + rxd_alignment_num: int =3D field(metadata=3DTextParser.find_int(r"RX= Ds > number alignment: (\d+)")) > + > + #: Current number of TX queues > + tx_queues_num: int =3D field(metadata=3DTextParser.find_int(r"Curren= t > number of TX queues: (\d+)")) > + #: Max possible TX queues > + max_tx_queues_num: int =3D field(metadata=3DTextParser.find_int(r"Ma= x > possible TX queues: (\d+)")) > + #: Max possible number of TXDs per queue > + max_queue_txd_num: int =3D field( > + metadata=3DTextParser.find_int(r"Max possible number of TXDs per > queue: (\d+)") > + ) > + #: Min possible number of TXDs per queue > + min_queue_txd_num: int =3D field( > + metadata=3DTextParser.find_int(r"Min possible number of TXDs per > queue: (\d+)") > + ) > + #: TXDs number alignment > + txd_alignment_num: int =3D field(metadata=3DTextParser.find_int(r"TX= Ds > number alignment: (\d+)")) > + #: Max segment number per packet > + max_packet_segment_num: int =3D field( > + metadata=3DTextParser.find_int(r"Max segment number per packet: > (\d+)") > + ) > + #: Max segment number per MTU/TSO > + max_mtu_segment_num: int =3D field( > + metadata=3DTextParser.find_int(r"Max segment number per MTU\/TSO= : > (\d+)") > + ) > + > + #: > + device_capabilities: DeviceCapabilitiesFlag =3D field( > + metadata=3DDeviceCapabilitiesFlag.make_parser(), > + ) > + #: > + device_error_handling_mode: DeviceErrorHandlingMode | None =3D field= ( > + default=3DNone, metadata=3DDeviceErrorHandlingMode.make_parser() > + ) > + #: > + device_private_info: str | None =3D field( > + default=3DNone, > + metadata=3D_make_device_private_info_parser(), > + ) > + > + #: > + hash_key_size: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"Hash key size i= n > bytes: (\d+)") > + ) > + #: > + redirection_table_size: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"Redirection tab= le > size: (\d+)") > + ) > + #: > + supported_rss_offload_flow_types: RSSOffloadTypesFlag =3D field( > + default=3DRSSOffloadTypesFlag(0), > metadata=3DRSSOffloadTypesFlag.make_parser() > + ) > + > + #: > + mac_address: str | None =3D field( > + default=3DNone, metadata=3DTextParser.find(r"MAC address: > ([A-Fa-f0-9:]+)") > + ) > + #: > + fw_version: str | None =3D field( > + default=3DNone, metadata=3DTextParser.find(r"Firmware-version: > ([^\r\n]+)") > + ) > + #: > + dev_args: str | None =3D field(default=3DNone, > metadata=3DTextParser.find(r"Devargs: ([^\r\n]+)")) > + #: Socket id of the memory allocation > + mem_alloc_socket_id: int | None =3D field( > + default=3DNone, > + metadata=3DTextParser.find_int(r"memory allocation on the socket= : > (\d+)"), > + ) > + #: > + mtu: int | None =3D field(default=3DNone, > metadata=3DTextParser.find_int(r"MTU: (\d+)")) > + > + #: > + vlan_offload: VLANOffloadFlag | None =3D field( > + default=3DNone, > + metadata=3DVLANOffloadFlag.make_parser(), > + ) > + > + #: Maximum size of RX buffer > + max_rx_bufsize: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"Maximum size of= RX > buffer: (\d+)") > + ) > + #: Maximum number of VFs > + max_vfs_num: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"Maximum number = of > VFs: (\d+)") > + ) > + #: Maximum number of VMDq pools > + max_vmdq_pools_num: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"Maximum number = of > VMDq pools: (\d+)") > + ) > + > + #: > + switch_name: str | None =3D field( > + default=3DNone, metadata=3DTextParser.find(r"Switch name: ([\r\n= ]+)") > + ) > + #: > + switch_domain_id: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"Switch domain I= d: > (\d+)") > + ) > + #: > + switch_port_id: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"Switch Port Id: > (\d+)") > + ) > + #: > + switch_rx_domain: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"Switch Rx domai= n: > (\d+)") > + ) > + > + > +@dataclass > +class TestPmdPortStats(TextParser): > + """Port statistics.""" > + > + #: > + port_id: int =3D field(metadata=3DTextParser.find_int(r"NIC statisti= cs > for port (\d+)")) > + > + #: > + rx_packets: int =3D > field(metadata=3DTextParser.find_int(r"RX-packets:\s+(\d+)")) > + #: > + rx_missed: int =3D > field(metadata=3DTextParser.find_int(r"RX-missed:\s+(\d+)")) > + #: > + rx_bytes: int =3D > field(metadata=3DTextParser.find_int(r"RX-bytes:\s+(\d+)")) > + #: > + rx_errors: int =3D > field(metadata=3DTextParser.find_int(r"RX-errors:\s+(\d+)")) > + #: > + rx_nombuf: int =3D > field(metadata=3DTextParser.find_int(r"RX-nombuf:\s+(\d+)")) > + > + #: > + tx_packets: int =3D > field(metadata=3DTextParser.find_int(r"TX-packets:\s+(\d+)")) > + #: > + tx_errors: int =3D > field(metadata=3DTextParser.find_int(r"TX-errors:\s+(\d+)")) > + #: > + tx_bytes: int =3D > field(metadata=3DTextParser.find_int(r"TX-bytes:\s+(\d+)")) > + > + #: > + rx_pps: int =3D field(metadata=3DTextParser.find_int(r"Rx-pps:\s+(\d= +)")) > + #: > + rx_bps: int =3D field(metadata=3DTextParser.find_int(r"Rx-bps:\s+(\d= +)")) > + > + #: > + tx_pps: int =3D field(metadata=3DTextParser.find_int(r"Tx-pps:\s+(\d= +)")) > + #: > + tx_bps: int =3D field(metadata=3DTextParser.find_int(r"Tx-bps:\s+(\d= +)")) > + > + > +@dataclass(kw_only=3DTrue) > +class FlowRule: > + """Class representation of flow rule parameters. > + > + This class represents the parameters of any flow rule as per the > + following pattern: > + > + [group {group_id}] [priority {level}] [ingress] [egress] > + [user_id {user_id}] pattern {item} [/ {item} [...]] / end > + actions {action} [/ {action} [...]] / end > + """ > + > + #: > + group_id: int | None =3D None > + #: > + priority_level: int | None =3D None > + #: > + direction: Literal["ingress", "egress"] > + #: > + user_id: int | None =3D None > + #: > + pattern: list[str] > + #: > + actions: list[str] > + > + def __str__(self) -> str: > + """Returns the string representation of this instance.""" > + ret =3D "" > + pattern =3D " / ".join(self.pattern) > + action =3D " / ".join(self.actions) > + if self.group_id is not None: > + ret +=3D f"group {self.group_id} " > + if self.priority_level is not None: > + ret +=3D f"priority {self.priority_level} " > + ret +=3D f"{self.direction} " > + if self.user_id is not None: > + ret +=3D f"user_id {self.user_id} " > + ret +=3D f"pattern {pattern} / end " > + ret +=3D f"actions {action} / end" > + return ret > + > + > +class PacketOffloadFlag(Flag): > + """Flag representing the Packet Offload Features Flags in DPDK. > + > + Values in this class are taken from the definitions in the RTE MBUF > core library in DPDK > + located in ``lib/mbuf/rte_mbuf_core.h``. It is expected that flag > values in this class will > + match the values they are set to in said DPDK library with one > exception; all values must be > + unique. For example, the definitions for unknown checksum flags in > ``rte_mbuf_core.h`` are all > + set to :data:`0`, but it is valuable to distinguish between them in > this framework. For this > + reason flags that are not unique in the DPDK library are set either > to values within the > + RTE_MBUF_F_FIRST_FREE-RTE_MBUF_F_LAST_FREE range for Rx or shifted > 61+ bits for Tx. > + > + References: > + DPDK lib: ``lib/mbuf/rte_mbuf_core.h`` > + """ > + > + # RX flags > + > + #: The RX packet is a 802.1q VLAN packet, and the tci has been saved > in mbuf->vlan_tci. If the > + #: flag RTE_MBUF_F_RX_VLAN_STRIPPED is also present, the VLAN header > has been stripped from > + #: mbuf data, else it is still present. > + RTE_MBUF_F_RX_VLAN =3D auto() > + > + #: RX packet with RSS hash result. > + RTE_MBUF_F_RX_RSS_HASH =3D auto() > + > + #: RX packet with FDIR match indicate. > + RTE_MBUF_F_RX_FDIR =3D auto() > + > + #: This flag is set when the outermost IP header checksum is detecte= d > as wrong by the hardware. > + RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD =3D 1 << 5 > + > + #: A vlan has been stripped by the hardware and its tci is saved in > mbuf->vlan_tci. This can > + #: only happen if vlan stripping is enabled in the RX configuration > of the PMD. When > + #: RTE_MBUF_F_RX_VLAN_STRIPPED is set, RTE_MBUF_F_RX_VLAN must also > be set. > + RTE_MBUF_F_RX_VLAN_STRIPPED =3D auto() > + > + #: No information about the RX IP checksum. Value is 0 in the DPDK > library. > + RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN =3D 1 << 23 > + #: The IP checksum in the packet is wrong. > + RTE_MBUF_F_RX_IP_CKSUM_BAD =3D 1 << 4 > + #: The IP checksum in the packet is valid. > + RTE_MBUF_F_RX_IP_CKSUM_GOOD =3D 1 << 7 > + #: The IP checksum is not correct in the packet data, but the > integrity of the IP header is > + #: verified. Value is RTE_MBUF_F_RX_IP_CKSUM_BAD | > RTE_MBUF_F_RX_IP_CKSUM_GOOD in the DPDK > + #: library. > + RTE_MBUF_F_RX_IP_CKSUM_NONE =3D 1 << 24 > + > + #: No information about the RX L4 checksum. Value is 0 in the DPDK > library. > + RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN =3D 1 << 25 > + #: The L4 checksum in the packet is wrong. > + RTE_MBUF_F_RX_L4_CKSUM_BAD =3D 1 << 3 > + #: The L4 checksum in the packet is valid. > + RTE_MBUF_F_RX_L4_CKSUM_GOOD =3D 1 << 8 > + #: The L4 checksum is not correct in the packet data, but the > integrity of the L4 data is > + #: verified. Value is RTE_MBUF_F_RX_L4_CKSUM_BAD | > RTE_MBUF_F_RX_L4_CKSUM_GOOD in the DPDK > + #: library. > + RTE_MBUF_F_RX_L4_CKSUM_NONE =3D 1 << 26 > + > + #: RX IEEE1588 L2 Ethernet PT Packet. > + RTE_MBUF_F_RX_IEEE1588_PTP =3D 1 << 9 > + #: RX IEEE1588 L2/L4 timestamped packet. > + RTE_MBUF_F_RX_IEEE1588_TMST =3D 1 << 10 > + > + #: FD id reported if FDIR match. > + RTE_MBUF_F_RX_FDIR_ID =3D 1 << 13 > + #: Flexible bytes reported if FDIR match. > + RTE_MBUF_F_RX_FDIR_FLX =3D 1 << 14 > + > + #: If both RTE_MBUF_F_RX_QINQ_STRIPPED and > RTE_MBUF_F_RX_VLAN_STRIPPED are set, the 2 VLANs > + #: have been stripped by the hardware. If RTE_MBUF_F_RX_QINQ_STRIPPE= D > is set and > + #: RTE_MBUF_F_RX_VLAN_STRIPPED is unset, only the outer VLAN is > removed from packet data. > + RTE_MBUF_F_RX_QINQ_STRIPPED =3D auto() > + > + #: When packets are coalesced by a hardware or virtual driver, this > flag can be set in the RX > + #: mbuf, meaning that the m->tso_segsz field is valid and is set to > the segment size of > + #: original packets. > + RTE_MBUF_F_RX_LRO =3D auto() > + > + #: Indicate that security offload processing was applied on the RX > packet. > + RTE_MBUF_F_RX_SEC_OFFLOAD =3D 1 << 18 > + #: Indicate that security offload processing failed on the RX packet= . > + RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED =3D auto() > + > + #: The RX packet is a double VLAN. If this flag is set, > RTE_MBUF_F_RX_VLAN must also be set. If > + #: the flag RTE_MBUF_F_RX_QINQ_STRIPPED is also present, both VLANs > headers have been stripped > + #: from mbuf data, else they are still present. > + RTE_MBUF_F_RX_QINQ =3D auto() > + > + #: No info about the outer RX L4 checksum. Value is 0 in the DPDK > library. > + RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN =3D 1 << 27 > + #: The outer L4 checksum in the packet is wrong > + RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD =3D 1 << 21 > + #: The outer L4 checksum in the packet is valid > + RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD =3D 1 << 22 > + #: Invalid outer L4 checksum state. Value is > + #: RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD | > RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD in the DPDK library. > + RTE_MBUF_F_RX_OUTER_L4_CKSUM_INVALID =3D 1 << 28 > + > + # TX flags > + > + #: Outer UDP checksum offload flag. This flag is used for enabling > outer UDP checksum in PMD. > + #: To use outer UDP checksum, the user either needs to enable the > following in mbuf: > + #: > + #: a) Fill outer_l2_len and outer_l3_len in mbuf. > + #: b) Set the RTE_MBUF_F_TX_OUTER_UDP_CKSUM flag. > + #: c) Set the RTE_MBUF_F_TX_OUTER_IPV4 or RTE_MBUF_F_TX_OUTER_IPV6 > flag. > + #: > + #: Or configure RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM offload flag. > + RTE_MBUF_F_TX_OUTER_UDP_CKSUM =3D 1 << 41 > + > + #: UDP Fragmentation Offload flag. This flag is used for enabling UD= P > fragmentation in SW or in > + #: HW. > + RTE_MBUF_F_TX_UDP_SEG =3D auto() > + > + #: Request security offload processing on the TX packet. To use Tx > security offload, the user > + #: needs to fill l2_len in mbuf indicating L2 header size and where > L3 header starts. > + #: Similarly, l3_len should also be filled along with ol_flags > reflecting current L3 type. > + RTE_MBUF_F_TX_SEC_OFFLOAD =3D auto() > + > + #: Offload the MACsec. This flag must be set by the application to > enable this offload feature > + #: for a packet to be transmitted. > + RTE_MBUF_F_TX_MACSEC =3D auto() > + > + # Bits 45:48 are used for the tunnel type in > ``lib/mbuf/rte_mbuf_core.h``, but some are modified > + # in this Flag to maintain uniqueness. The tunnel type must be > specified for TSO or checksum on > + # the inner part of tunnel packets. These flags can be used with > RTE_MBUF_F_TX_TCP_SEG for TSO, > + # or RTE_MBUF_F_TX_xxx_CKSUM. The mbuf fields for inner and outer > header lengths are required: > + # outer_l2_len, outer_l3_len, l2_len, l3_len, l4_len and tso_segsz > for TSO. > + > + #: > + RTE_MBUF_F_TX_TUNNEL_VXLAN =3D 1 << 45 > + #: > + RTE_MBUF_F_TX_TUNNEL_GRE =3D 1 << 46 > + #: Value is 3 << 45 in the DPDK library. > + RTE_MBUF_F_TX_TUNNEL_IPIP =3D 1 << 61 > + #: > + RTE_MBUF_F_TX_TUNNEL_GENEVE =3D 1 << 47 > + #: TX packet with MPLS-in-UDP RFC 7510 header. Value is 5 << 45 in > the DPDK library. > + RTE_MBUF_F_TX_TUNNEL_MPLSINUDP =3D 1 << 62 > + #: Value is 6 << 45 in the DPDK library. > + RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE =3D 1 << 63 > + #: Value is 7 << 45 in the DPDK library. > + RTE_MBUF_F_TX_TUNNEL_GTP =3D 1 << 64 > + #: > + RTE_MBUF_F_TX_TUNNEL_ESP =3D 1 << 48 > + #: Generic IP encapsulated tunnel type, used for TSO and checksum > offload. This can be used for > + #: tunnels which are not standards or listed above. It is preferred > to use specific tunnel > + #: flags like RTE_MBUF_F_TX_TUNNEL_GRE or RTE_MBUF_F_TX_TUNNEL_IPIP > if possible. The ethdev > + #: must be configured with RTE_ETH_TX_OFFLOAD_IP_TNL_TSO. Outer and > inner checksums are done > + #: according to the existing flags like RTE_MBUF_F_TX_xxx_CKSUM. > Specific tunnel headers that > + #: contain payload length, sequence id or checksum are not expected > to be updated. Value is > + #: 0xD << 45 in the DPDK library. > + RTE_MBUF_F_TX_TUNNEL_IP =3D 1 << 65 > + #: Generic UDP encapsulated tunnel type, used for TSO and checksum > offload. UDP tunnel type > + #: implies outer IP layer. It can be used for tunnels which are not > standards or listed above. > + #: It is preferred to use specific tunnel flags like > RTE_MBUF_F_TX_TUNNEL_VXLAN if possible. > + #: The ethdev must be configured with RTE_ETH_TX_OFFLOAD_UDP_TNL_TSO= . > Outer and inner checksums > + #: are done according to the existing flags like > RTE_MBUF_F_TX_xxx_CKSUM. Specific tunnel > + #: headers that contain payload length, sequence id or checksum are > not expected to be updated. > + #: value is 0xE << 45 in the DPDK library. > + RTE_MBUF_F_TX_TUNNEL_UDP =3D 1 << 66 > + > + #: Double VLAN insertion (QinQ) request to driver, driver may offloa= d > the insertion based on > + #: device capability. Mbuf 'vlan_tci' & 'vlan_tci_outer' must be > valid when this flag is set. > + RTE_MBUF_F_TX_QINQ =3D 1 << 49 > + > + #: TCP segmentation offload. To enable this offload feature for a > packet to be transmitted on > + #: hardware supporting TSO: > + #: > + #: - set the RTE_MBUF_F_TX_TCP_SEG flag in mbuf->ol_flags (this fla= g > implies > + #: RTE_MBUF_F_TX_TCP_CKSUM) > + #: - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6 > + #: * if it's IPv4, set the RTE_MBUF_F_TX_IP_CKSUM flag > + #: - fill the mbuf offload information: l2_len, l3_len, l4_len, > tso_segsz > + RTE_MBUF_F_TX_TCP_SEG =3D auto() > + > + #: TX IEEE1588 packet to timestamp. > + RTE_MBUF_F_TX_IEEE1588_TMST =3D auto() > + > + # Bits 52+53 used for L4 packet type with checksum enabled in > ``lib/mbuf/rte_mbuf_core.h`` but > + # some values must be modified in this framework to maintain > uniqueness. To use hardware > + # L4 checksum offload, the user needs to: > + # > + # - fill l2_len and l3_len in mbuf > + # - set the flags RTE_MBUF_F_TX_TCP_CKSUM, RTE_MBUF_F_TX_SCTP_CKSUM = or > + # RTE_MBUF_F_TX_UDP_CKSUM > + # - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6 > + > + #: Disable L4 cksum of TX pkt. Value is 0 in the DPDK library. > + RTE_MBUF_F_TX_L4_NO_CKSUM =3D 1 << 67 > + #: TCP cksum of TX pkt. Computed by NIC. > + RTE_MBUF_F_TX_TCP_CKSUM =3D 1 << 52 > + #: SCTP cksum of TX pkt. Computed by NIC. > + RTE_MBUF_F_TX_SCTP_CKSUM =3D 1 << 53 > + #: UDP cksum of TX pkt. Computed by NIC. Value is 3 << 52 in the DPD= K > library. > + RTE_MBUF_F_TX_UDP_CKSUM =3D 1 << 68 > + > + #: Offload the IP checksum in the hardware. The flag > RTE_MBUF_F_TX_IPV4 should also be set by > + #: the application, although a PMD will only check > RTE_MBUF_F_TX_IP_CKSUM. > + RTE_MBUF_F_TX_IP_CKSUM =3D 1 << 54 > + > + #: Packet is IPv4. This flag must be set when using any offload > feature (TSO, L3 or L4 > + #: checksum) to tell the NIC that the packet is an IPv4 packet. If > the packet is a tunneled > + #: packet, this flag is related to the inner headers. > + RTE_MBUF_F_TX_IPV4 =3D auto() > + #: Packet is IPv6. This flag must be set when using an offload > feature (TSO or L4 checksum) to > + #: tell the NIC that the packet is an IPv6 packet. If the packet is = a > tunneled packet, this > + #: flag is related to the inner headers. > + RTE_MBUF_F_TX_IPV6 =3D auto() > + #: VLAN tag insertion request to driver, driver may offload the > insertion based on the device > + #: capability. mbuf 'vlan_tci' field must be valid when this flag is > set. > + RTE_MBUF_F_TX_VLAN =3D auto() > + > + #: Offload the IP checksum of an external header in the hardware. Th= e > flag > + #: RTE_MBUF_F_TX_OUTER_IPV4 should also be set by the application, > although a PMD will only > + #: check RTE_MBUF_F_TX_OUTER_IP_CKSUM. > + RTE_MBUF_F_TX_OUTER_IP_CKSUM =3D auto() > + #: Packet outer header is IPv4. This flag must be set when using any > outer offload feature (L3 > + #: or L4 checksum) to tell the NIC that the outer header of the > tunneled packet is an IPv4 > + #: packet. > + RTE_MBUF_F_TX_OUTER_IPV4 =3D auto() > + #: Packet outer header is IPv6. This flag must be set when using any > outer offload feature (L4 > + #: checksum) to tell the NIC that the outer header of the tunneled > packet is an IPv6 packet. > + RTE_MBUF_F_TX_OUTER_IPV6 =3D auto() > + > + @classmethod > + def from_list_string(cls, names: str) -> Self: > + """Makes a flag from a whitespace-separated list of names. > + > + Args: > + names: a whitespace-separated list containing the members of > this flag. > + > + Returns: > + An instance of this flag. > + """ > + flag =3D cls(0) > + for name in names.split(): > + flag |=3D cls.from_str(name) > + return flag > + > + @classmethod > + def from_str(cls, name: str) -> Self: > + """Makes a flag matching the supplied name. > + > + Args: > + name: a valid member of this flag in text > + Returns: > + An instance of this flag. > + """ > + member_name =3D name.strip().replace("-", "_") > + return cls[member_name] > + > + @classmethod > + def make_parser(cls) -> ParserFn: > + """Makes a parser function. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > + parser function that makes an instance of this flag from > text. > + """ > + return TextParser.wrap( > + TextParser.find(r"ol_flags: ([^\n]+)"), > + cls.from_list_string, > + ) > + > + > +class RtePTypes(Flag): > + """Flag representing possible packet types in DPDK verbose output. > + > + Values in this class are derived from definitions in the RTE MBUF > ptype library in DPDK located > + in ``lib/mbuf/rte_mbuf_ptype.h``. Specifically, the names of values > in this class should match > + the possible return options from the functions > ``rte_get_ptype_*_name`` in ``rte_mbuf_ptype.c``. > + > + References: > + DPDK lib: ``lib/mbuf/rte_mbuf_ptype.h`` > + DPDK ptype name formatting functions: > ``lib/mbuf/rte_mbuf_ptype.c:rte_get_ptype_*_name()`` > + """ > + > + # L2 > + #: Ethernet packet type. This is used for outer packet for tunneling > cases. > + L2_ETHER =3D auto() > + #: Ethernet packet type for time sync. > + L2_ETHER_TIMESYNC =3D auto() > + #: ARP (Address Resolution Protocol) packet type. > + L2_ETHER_ARP =3D auto() > + #: LLDP (Link Layer Discovery Protocol) packet type. > + L2_ETHER_LLDP =3D auto() > + #: NSH (Network Service Header) packet type. > + L2_ETHER_NSH =3D auto() > + #: VLAN packet type. > + L2_ETHER_VLAN =3D auto() > + #: QinQ packet type. > + L2_ETHER_QINQ =3D auto() > + #: PPPOE packet type. > + L2_ETHER_PPPOE =3D auto() > + #: FCoE packet type.. > + L2_ETHER_FCOE =3D auto() > + #: MPLS packet type. > + L2_ETHER_MPLS =3D auto() > + #: No L2 packet information. > + L2_UNKNOWN =3D auto() > + > + # L3 > + #: IP (Internet Protocol) version 4 packet type. This is used for > outer packet for tunneling > + #: cases, and does not contain any header option. > + L3_IPV4 =3D auto() > + #: IP (Internet Protocol) version 4 packet type. This is used for > outer packet for tunneling > + #: cases, and contains header options. > + L3_IPV4_EXT =3D auto() > + #: IP (Internet Protocol) version 6 packet type. This is used for > outer packet for tunneling > + #: cases, and does not contain any extension header. > + L3_IPV6 =3D auto() > + #: IP (Internet Protocol) version 4 packet type. This is used for > outer packet for tunneling > + #: cases, and may or maynot contain header options. > + L3_IPV4_EXT_UNKNOWN =3D auto() > + #: IP (Internet Protocol) version 6 packet type. This is used for > outer packet for tunneling > + #: cases, and contains extension headers. > + L3_IPV6_EXT =3D auto() > + #: IP (Internet Protocol) version 6 packet type. This is used for > outer packet for tunneling > + #: cases, and may or maynot contain extension headers. > + L3_IPV6_EXT_UNKNOWN =3D auto() > + #: No L3 packet information. > + L3_UNKNOWN =3D auto() > + > + # L4 > + #: TCP (Transmission Control Protocol) packet type. This is used for > outer packet for tunneling > + #: cases. > + L4_TCP =3D auto() > + #: UDP (User Datagram Protocol) packet type. This is used for outer > packet for tunneling cases. > + L4_UDP =3D auto() > + #: Fragmented IP (Internet Protocol) packet type. This is used for > outer packet for tunneling > + #: cases and refers to those packets of any IP types which can be > recognized as fragmented. A > + #: fragmented packet cannot be recognized as any other L4 types > (RTE_PTYPE_L4_TCP, > + #: RTE_PTYPE_L4_UDP, RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP, > RTE_PTYPE_L4_NONFRAG). > + L4_FRAG =3D auto() > + #: SCTP (Stream Control Transmission Protocol) packet type. This is > used for outer packet for > + #: tunneling cases. > + L4_SCTP =3D auto() > + #: ICMP (Internet Control Message Protocol) packet type. This is use= d > for outer packet for > + #: tunneling cases. > + L4_ICMP =3D auto() > + #: Non-fragmented IP (Internet Protocol) packet type. This is used > for outer packet for > + #: tunneling cases and refers to those packets of any IP types, that > cannot be recognized as > + #: any of the above L4 types (RTE_PTYPE_L4_TCP, RTE_PTYPE_L4_UDP, > RTE_PTYPE_L4_FRAG, > + #: RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP). > + L4_NONFRAG =3D auto() > + #: IGMP (Internet Group Management Protocol) packet type. > + L4_IGMP =3D auto() > + #: No L4 packet information. > + L4_UNKNOWN =3D auto() > + > + # Tunnel > + #: IP (Internet Protocol) in IP (Internet Protocol) tunneling packet > type. > + TUNNEL_IP =3D auto() > + #: GRE (Generic Routing Encapsulation) tunneling packet type. > + TUNNEL_GRE =3D auto() > + #: VXLAN (Virtual eXtensible Local Area Network) tunneling packet > type. > + TUNNEL_VXLAN =3D auto() > + #: NVGRE (Network Virtualization using Generic Routing Encapsulation= ) > tunneling packet type. > + TUNNEL_NVGRE =3D auto() > + #: GENEVE (Generic Network Virtualization Encapsulation) tunneling > packet type. > + TUNNEL_GENEVE =3D auto() > + #: Tunneling packet type of Teredo, VXLAN (Virtual eXtensible Local > Area Network) or GRE > + #: (Generic Routing Encapsulation) could be recognized as this packe= t > type, if they can not be > + #: recognized independently as of hardware capability. > + TUNNEL_GRENAT =3D auto() > + #: GTP-C (GPRS Tunnelling Protocol) control tunneling packet type. > + TUNNEL_GTPC =3D auto() > + #: GTP-U (GPRS Tunnelling Protocol) user data tunneling packet type. > + TUNNEL_GTPU =3D auto() > + #: ESP (IP Encapsulating Security Payload) tunneling packet type. > + TUNNEL_ESP =3D auto() > + #: L2TP (Layer 2 Tunneling Protocol) tunneling packet type. > + TUNNEL_L2TP =3D auto() > + #: VXLAN-GPE (VXLAN Generic Protocol Extension) tunneling packet typ= e. > + TUNNEL_VXLAN_GPE =3D auto() > + #: MPLS-in-UDP tunneling packet type (RFC 7510). > + TUNNEL_MPLS_IN_UDP =3D auto() > + #: MPLS-in-GRE tunneling packet type (RFC 4023). > + TUNNEL_MPLS_IN_GRE =3D auto() > + #: No tunnel information found on the packet. > + TUNNEL_UNKNOWN =3D auto() > + > + # Inner L2 > + #: Ethernet packet type. This is used for inner packet type only. > + INNER_L2_ETHER =3D auto() > + #: Ethernet packet type with VLAN (Virtual Local Area Network) tag. > + INNER_L2_ETHER_VLAN =3D auto() > + #: QinQ packet type. > + INNER_L2_ETHER_QINQ =3D auto() > + #: No inner L2 information found on the packet. > + INNER_L2_UNKNOWN =3D auto() > + > + # Inner L3 > + #: IP (Internet Protocol) version 4 packet type. This is used for > inner packet only, and does > + #: not contain any header option. > + INNER_L3_IPV4 =3D auto() > + #: IP (Internet Protocol) version 4 packet type. This is used for > inner packet only, and > + #: contains header options. > + INNER_L3_IPV4_EXT =3D auto() > + #: IP (Internet Protocol) version 6 packet type. This is used for > inner packet only, and does > + #: not contain any extension header. > + INNER_L3_IPV6 =3D auto() > + #: IP (Internet Protocol) version 4 packet type. This is used for > inner packet only, and may or > + #: may not contain header options. > + INNER_L3_IPV4_EXT_UNKNOWN =3D auto() > + #: IP (Internet Protocol) version 6 packet type. This is used for > inner packet only, and > + #: contains extension headers. > + INNER_L3_IPV6_EXT =3D auto() > + #: IP (Internet Protocol) version 6 packet type. This is used for > inner packet only, and may or > + #: may not contain extension headers. > + INNER_L3_IPV6_EXT_UNKNOWN =3D auto() > + #: No inner L3 information found on the packet. > + INNER_L3_UNKNOWN =3D auto() > + > + # Inner L4 > + #: TCP (Transmission Control Protocol) packet type. This is used for > inner packet only. > + INNER_L4_TCP =3D auto() > + #: UDP (User Datagram Protocol) packet type. This is used for inner > packet only. > + INNER_L4_UDP =3D auto() > + #: Fragmented IP (Internet Protocol) packet type. This is used for > inner packet only, and may > + #: or maynot have a layer 4 packet. > + INNER_L4_FRAG =3D auto() > + #: SCTP (Stream Control Transmission Protocol) packet type. This is > used for inner packet only. > + INNER_L4_SCTP =3D auto() > + #: ICMP (Internet Control Message Protocol) packet type. This is use= d > for inner packet only. > + INNER_L4_ICMP =3D auto() > + #: Non-fragmented IP (Internet Protocol) packet type. It is used for > inner packet only, and may > + #: or may not have other unknown layer 4 packet types. > + INNER_L4_NONFRAG =3D auto() > + #: No inner L4 information found on the packet. > + INNER_L4_UNKNOWN =3D auto() > + > + @classmethod > + def from_list_string(cls, names: str) -> Self: > + """Makes a flag from a whitespace-separated list of names. > + > + Args: > + names: a whitespace-separated list containing the members of > this flag. > + > + Returns: > + An instance of this flag. > + """ > + flag =3D cls(0) > + for name in names.split(): > + flag |=3D cls.from_str(name) > + return flag > + > + @classmethod > + def from_str(cls, name: str) -> Self: > + """Makes a flag matching the supplied name. > + > + Args: > + name: a valid member of this flag in text > + Returns: > + An instance of this flag. > + """ > + member_name =3D name.strip().replace("-", "_") > + return cls[member_name] > + > + @classmethod > + def make_parser(cls, hw: bool) -> ParserFn: > + """Makes a parser function. > + > + Args: > + hw: Whether to make a parser for hardware ptypes or software > ptypes. If :data:`True`, > + hardware ptypes will be collected, otherwise software > pytpes will. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > + parser function that makes an instance of this flag from > text. > + """ > + return TextParser.wrap( > + TextParser.find(f"{'hw' if hw else 'sw'} ptype: ([^-]+)"), > + cls.from_list_string, > + ) > + > + > +@dataclass > +class TestPmdVerbosePacket(TextParser): > + """Packet information provided by verbose output in Testpmd. > + > + This dataclass expects that packet information be prepended with the > starting line of packet > + bursts. Specifically, the line that reads "port X/queue Y: > sent/received Z packets". > + """ > + > + #: ID of the port that handled the packet. > + port_id: int =3D field(metadata=3DTextParser.find_int(r"port (\d+)/q= ueue > \d+")) > + #: ID of the queue that handled the packet. > + queue_id: int =3D field(metadata=3DTextParser.find_int(r"port \d+/qu= eue > (\d+)")) > + #: Whether the packet was received or sent by the queue/port. > + was_received: bool =3D field(metadata=3DTextParser.find(r"received \= d+ > packets")) > + #: > + src_mac: str =3D > field(metadata=3DTextParser.find(f"src=3D({REGEX_FOR_MAC_ADDRESS})")) > + #: > + dst_mac: str =3D > field(metadata=3DTextParser.find(f"dst=3D({REGEX_FOR_MAC_ADDRESS})")) > + #: Memory pool the packet was handled on. > + pool: str =3D field(metadata=3DTextParser.find(r"pool=3D(\S+)")) > + #: Packet type in hex. > + p_type: int =3D > field(metadata=3DTextParser.find_int(r"type=3D(0x[a-fA-F\d]+)")) > + #: > + length: int =3D field(metadata=3DTextParser.find_int(r"length=3D(\d+= )")) > + #: Number of segments in the packet. > + nb_segs: int =3D field(metadata=3DTextParser.find_int(r"nb_segs=3D(\= d+)")) > + #: Hardware packet type. > + hw_ptype: RtePTypes =3D field(metadata=3DRtePTypes.make_parser(hw=3D= True)) > + #: Software packet type. > + sw_ptype: RtePTypes =3D field(metadata=3DRtePTypes.make_parser(hw=3D= False)) > + #: > + l2_len: int =3D field(metadata=3DTextParser.find_int(r"l2_len=3D(\d+= )")) > + #: > + ol_flags: PacketOffloadFlag =3D > field(metadata=3DPacketOffloadFlag.make_parser()) > + #: RSS hash of the packet in hex. > + rss_hash: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"RSS > hash=3D(0x[a-fA-F\d]+)") > + ) > + #: RSS queue that handled the packet in hex. > + rss_queue: int | None =3D field( > + default=3DNone, metadata=3DTextParser.find_int(r"RSS > queue=3D(0x[a-fA-F\d]+)") > + ) > + #: > + l3_len: int | None =3D field(default=3DNone, > metadata=3DTextParser.find_int(r"l3_len=3D(\d+)")) > + #: > + l4_len: int | None =3D field(default=3DNone, > metadata=3DTextParser.find_int(r"l4_len=3D(\d+)")) > + #: > + l4_dport: int | None =3D field( > + default=3DNone, > + metadata=3DTextParser.find_int(r"Destination (?:TCP|UDP) > port=3D(\d+)"), > + ) > + > + > +class RxOffloadCapability(Flag): > + """Rx offload capabilities of a device. > + > + The flags are taken from ``lib/ethdev/rte_ethdev.h``. > + They're prefixed with ``RTE_ETH_RX_OFFLOAD`` in > ``lib/ethdev/rte_ethdev.h`` > + instead of ``RX_OFFLOAD``, which is what testpmd changes the prefix > to. > + The values are not contiguous, so the correspondence is preserved > + by specifying concrete values interspersed between auto() values. > + > + The ``RX_OFFLOAD`` prefix has been preserved so that the same flag > names can be used > + in :class:`NicCapability`. The prefix is needed in > :class:`NicCapability` since there's > + no other qualifier which would sufficiently distinguish it from othe= r > capabilities. > + > + References: > + DPDK lib: ``lib/ethdev/rte_ethdev.h`` > + testpmd display function: > ``app/test-pmd/cmdline.c:print_rx_offloads()`` > + """ > + > + #: > + RX_OFFLOAD_VLAN_STRIP =3D auto() > + #: Device supports L3 checksum offload. > + RX_OFFLOAD_IPV4_CKSUM =3D auto() > + #: Device supports L4 checksum offload. > + RX_OFFLOAD_UDP_CKSUM =3D auto() > + #: Device supports L4 checksum offload. > + RX_OFFLOAD_TCP_CKSUM =3D auto() > + #: Device supports Large Receive Offload. > + RX_OFFLOAD_TCP_LRO =3D auto() > + #: Device supports QinQ (queue in queue) offload. > + RX_OFFLOAD_QINQ_STRIP =3D auto() > + #: Device supports inner packet L3 checksum. > + RX_OFFLOAD_OUTER_IPV4_CKSUM =3D auto() > + #: Device supports MACsec. > + RX_OFFLOAD_MACSEC_STRIP =3D auto() > + #: Device supports filtering of a VLAN Tag identifier. > + RX_OFFLOAD_VLAN_FILTER =3D 1 << 9 > + #: Device supports VLAN offload. > + RX_OFFLOAD_VLAN_EXTEND =3D auto() > + #: Device supports receiving segmented mbufs. > + RX_OFFLOAD_SCATTER =3D 1 << 13 > + #: Device supports Timestamp. > + RX_OFFLOAD_TIMESTAMP =3D auto() > + #: Device supports crypto processing while packet is received in NIC= . > + RX_OFFLOAD_SECURITY =3D auto() > + #: Device supports CRC stripping. > + RX_OFFLOAD_KEEP_CRC =3D auto() > + #: Device supports L4 checksum offload. > + RX_OFFLOAD_SCTP_CKSUM =3D auto() > + #: Device supports inner packet L4 checksum. > + RX_OFFLOAD_OUTER_UDP_CKSUM =3D auto() > + #: Device supports RSS hashing. > + RX_OFFLOAD_RSS_HASH =3D auto() > + #: Device supports > + RX_OFFLOAD_BUFFER_SPLIT =3D auto() > + #: Device supports all checksum capabilities. > + RX_OFFLOAD_CHECKSUM =3D RX_OFFLOAD_IPV4_CKSUM | RX_OFFLOAD_UDP_CKSUM= | > RX_OFFLOAD_TCP_CKSUM > + #: Device supports all VLAN capabilities. > + RX_OFFLOAD_VLAN =3D ( > + RX_OFFLOAD_VLAN_STRIP > + | RX_OFFLOAD_VLAN_FILTER > + | RX_OFFLOAD_VLAN_EXTEND > + | RX_OFFLOAD_QINQ_STRIP > + ) > + > + @classmethod > + def from_string(cls, line: str) -> Self: > + """Make an instance from a string containing the flag names > separated with a space. > + > + Args: > + line: The line to parse. > + > + Returns: > + A new instance containing all found flags. > + """ > + flag =3D cls(0) > + for flag_name in line.split(): > + flag |=3D cls[f"RX_OFFLOAD_{flag_name}"] > + return flag > + > + @classmethod > + def make_parser(cls, per_port: bool) -> ParserFn: > + """Make a parser function. > + > + Args: > + per_port: If :data:`True`, will return capabilities per port= . > If :data:`False`, > + will return capabilities per queue. > + > + Returns: > + ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > + parser function that makes an instance of this flag from > text. > + """ > + granularity =3D "Port" if per_port else "Queue" > + return TextParser.wrap( > + TextParser.find(rf"Per {granularity}\s+:(.*)$", re.MULTILINE= ), > + cls.from_string, > + ) > + > + > +@dataclass > +class RxOffloadCapabilities(TextParser): > + """The result of testpmd's ``show port rx_offload > capabilities`` command. > + > + References: > + testpmd command function: > ``app/test-pmd/cmdline.c:cmd_rx_offload_get_capa()`` > + testpmd display function: > ``app/test-pmd/cmdline.c:cmd_rx_offload_get_capa_parsed()`` > + """ > + > + #: > + port_id: int =3D field( > + metadata=3DTextParser.find_int(r"Rx Offloading Capabilities of p= ort > (\d+) :") > + ) > + #: Per-queue Rx offload capabilities. > + per_queue: RxOffloadCapability =3D > field(metadata=3DRxOffloadCapability.make_parser(False)) > + #: Capabilities other than per-queue Rx offload capabilities. > + per_port: RxOffloadCapability =3D > field(metadata=3DRxOffloadCapability.make_parser(True)) > + > + > +@dataclass > +class TestPmdPortFlowCtrl(TextParser): > + """Class representing a port's flow control parameters. > + > + The parameters can also be parsed from the output of ``show port > flow_ctrl``. > + """ > + > + #: Enable Reactive Extensions. > + rx: bool =3D field(default=3DFalse, metadata=3DTextParser.find(r"Rx = pause: > on")) > + #: Enable Transmit. > + tx: bool =3D field(default=3DFalse, metadata=3DTextParser.find(r"Tx = pause: > on")) > + #: High threshold value to trigger XOFF. > + high_water: int =3D field( > + default=3D0, metadata=3DTextParser.find_int(r"High waterline: > (0x[a-fA-F\d]+)") > + ) > + #: Low threshold value to trigger XON. > + low_water: int =3D field( > + default=3D0, metadata=3DTextParser.find_int(r"Low waterline: > (0x[a-fA-F\d]+)") > + ) > + #: Pause quota in the Pause frame. > + pause_time: int =3D field(default=3D0, > metadata=3DTextParser.find_int(r"Pause time: (0x[a-fA-F\d]+)")) > + #: Send XON frame. > + send_xon: bool =3D field(default=3DFalse, metadata=3DTextParser.find= (r"Tx > pause: on")) > + #: Enable receiving MAC control frames. > + mac_ctrl_frame_fwd: bool =3D field(default=3DFalse, > metadata=3DTextParser.find(r"Tx pause: on")) > + #: Change the auto-negotiation parameter. > + autoneg: bool =3D field(default=3DFalse, > metadata=3DTextParser.find(r"Autoneg: on")) > + > + def __str__(self) -> str: > + """Returns the string representation of this instance.""" > + ret =3D ( > + f"rx {'on' if self.rx else 'off'} " > + f"tx {'on' if self.tx else 'off'} " > + f"{self.high_water} " > + f"{self.low_water} " > + f"{self.pause_time} " > + f"{1 if self.send_xon else 0} " > + f"mac_ctrl_frame_fwd {'on' if self.mac_ctrl_frame_fwd else > 'off'} " > + f"autoneg {'on' if self.autoneg else 'off'}" > + ) > + return ret > diff --git a/dts/framework/config/__init__.py > b/dts/framework/config/__init__.py > index 1ec744d1d4..d2f0138e4a 100644 > --- a/dts/framework/config/__init__.py > +++ b/dts/framework/config/__init__.py > @@ -28,7 +28,6 @@ > and makes it thread safe should we ever want to move in that > direction. > """ > > -import os > from pathlib import Path > from typing import TYPE_CHECKING, Annotated, Any, Literal, TypeVar, cast > > @@ -43,7 +42,7 @@ > from .test_run import TestRunConfiguration, > create_test_suites_config_model > > # Import only if type checking or building docs, to prevent circular > imports. > -if TYPE_CHECKING or os.environ.get("DTS_DOC_BUILD"): > +if TYPE_CHECKING: > from framework.test_suite import BaseConfig > > NodesConfig =3D Annotated[list[NodeConfiguration], Field(min_length=3D1)= ] > diff --git a/dts/framework/params/eal.py b/dts/framework/params/eal.py > index b90ff33dcf..e84a20f02f 100644 > --- a/dts/framework/params/eal.py > +++ b/dts/framework/params/eal.py > @@ -4,15 +4,17 @@ > """Module representing the DPDK EAL-related parameters.""" > > from dataclasses import dataclass, field > -from typing import Literal > +from typing import TYPE_CHECKING, Literal > > from framework.params import Params, Switch > from framework.testbed_model.cpu import LogicalCoreList > -from framework.testbed_model.port import Port > from framework.testbed_model.virtual_device import VirtualDevice > > +if TYPE_CHECKING: > + from framework.testbed_model.port import Port > > -def _port_to_pci(port: Port) -> str: > + > +def _port_to_pci(port: "Port") -> str: > return port.pci > > > @@ -42,11 +44,11 @@ class EalParams(Params): > vdevs: list[VirtualDevice] | None =3D field( > default=3DNone, metadata=3DParams.multiple() | Params.long("vdev= ") > ) > - allowed_ports: list[Port] | None =3D field( > + allowed_ports: list["Port"] | None =3D field( > default=3DNone, > metadata=3DParams.convert_value(_port_to_pci) | Params.multiple(= ) | > Params.short("a"), > ) > - blocked_ports: list[Port] | None =3D field( > + blocked_ports: list["Port"] | None =3D field( > default=3DNone, > metadata=3DParams.convert_value(_port_to_pci) | Params.multiple(= ) | > Params.short("b"), > ) > diff --git a/dts/framework/params/types.py b/dts/framework/params/types.p= y > index 87d11502e8..5bc4bd37d9 100644 > --- a/dts/framework/params/types.py > +++ b/dts/framework/params/types.py > @@ -15,8 +15,7 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict])= : > from pathlib import PurePath > from typing import TypedDict > > -from framework.params import Switch, YesNoSwitch > -from framework.params.testpmd import ( > +from api.testpmd.config import ( > AnonMempoolAllocationMode, > EthPeer, > Event, > @@ -37,6 +36,7 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict])= : > TXRingParams, > TxUDPPortPair, > ) > +from framework.params import Switch, YesNoSwitch > from framework.testbed_model.cpu import LogicalCoreList > from framework.testbed_model.port import Port > from framework.testbed_model.virtual_device import VirtualDevice > diff --git a/dts/framework/remote_session/__init__.py > b/dts/framework/remote_session/__init__.py > index 1a5cf6abd3..394c88b25e 100644 > --- a/dts/framework/remote_session/__init__.py > +++ b/dts/framework/remote_session/__init__.py > @@ -11,47 +11,3 @@ > The interactive sessions open an interactive shell which is continuously > open, > allowing it to send and receive data within that particular shell. > """ > - > -from framework.config.node import NodeConfiguration > -from framework.logger import DTSLogger > - > -from .interactive_remote_session import InteractiveRemoteSession > -from .remote_session import RemoteSession > -from .ssh_session import SSHSession > - > - > -def create_remote_session( > - node_config: NodeConfiguration, name: str, logger: DTSLogger > -) -> RemoteSession: > - """Factory for non-interactive remote sessions. > - > - The function returns an SSH session, but will be extended if support > - for other protocols is added. > - > - Args: > - node_config: The test run configuration of the node to connect t= o. > - name: The name of the session. > - logger: The logger instance this session will use. > - > - Returns: > - The SSH remote session. > - """ > - return SSHSession(node_config, name, logger) > - > - > -def create_interactive_session( > - node_config: NodeConfiguration, logger: DTSLogger > -) -> InteractiveRemoteSession: > - """Factory for interactive remote sessions. > - > - The function returns an interactive SSH session, but will be extende= d > if support > - for other protocols is added. > - > - Args: > - node_config: The test run configuration of the node to connect t= o. > - logger: The logger instance this session will use. > - > - Returns: > - The interactive SSH remote session. > - """ > - return InteractiveRemoteSession(node_config, logger) > diff --git a/dts/framework/remote_session/testpmd_shell.py > b/dts/framework/remote_session/testpmd_shell.py > deleted file mode 100644 > index ad8cb273dc..0000000000 > --- a/dts/framework/remote_session/testpmd_shell.py > +++ /dev/null > @@ -1,2844 +0,0 @@ > -# SPDX-License-Identifier: BSD-3-Clause > -# Copyright(c) 2023 University of New Hampshire > -# Copyright(c) 2023 PANTHEON.tech s.r.o. > -# Copyright(c) 2024 Arm Limited > - > -"""Testpmd interactive shell. > - > -Typical usage example in a TestSuite:: > - > - testpmd_shell =3D TestPmdShell(self.sut_node) > - devices =3D testpmd_shell.get_devices() > - for device in devices: > - print(device) > - testpmd_shell.close() > -""" > - > -import functools > -import re > -import time > -from collections.abc import Callable, MutableSet > -from dataclasses import dataclass, field > -from enum import Flag, auto > -from os import environ > -from pathlib import PurePath > -from typing import TYPE_CHECKING, Any, ClassVar, Concatenate, Literal, > ParamSpec, Tuple, TypeAlias > - > -from framework.context import get_ctx > -from framework.remote_session.interactive_shell import only_active > -from framework.testbed_model.topology import TopologyType > - > -if TYPE_CHECKING or environ.get("DTS_DOC_BUILD"): > - from enum import Enum as NoAliasEnum > -else: > - from aenum import NoAliasEnum > - > -from typing_extensions import Self, Unpack > - > -from framework.exception import InteractiveCommandExecutionError, > InternalError > -from framework.params.testpmd import PortTopology, SimpleForwardingModes= , > TestPmdParams > -from framework.params.types import TestPmdParamsDict > -from framework.parser import ParserFn, TextParser > -from framework.remote_session.dpdk_shell import DPDKShell > -from framework.settings import SETTINGS > -from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum > - > -P =3D ParamSpec("P") > -TestPmdShellMethod =3D Callable[Concatenate["TestPmdShell", P], Any] > - > -TestPmdShellCapabilityMethod: TypeAlias =3D Callable[ > - ["TestPmdShell", MutableSet["NicCapability"], > MutableSet["NicCapability"]], None > -] > - > -TestPmdShellDecorator: TypeAlias =3D Callable[[TestPmdShellMethod], > TestPmdShellMethod] > - > -TestPmdShellNicCapability =3D tuple[TestPmdShellCapabilityMethod, > TestPmdShellDecorator | None] > - > - > -class TestPmdDevice: > - """The data of a device that testpmd can recognize. > - > - Attributes: > - pci_address: The PCI address of the device. > - """ > - > - pci_address: str > - > - def __init__(self, pci_address_line: str): > - """Initialize the device from the testpmd output line string. > - > - Args: > - pci_address_line: A line of testpmd output that contains a > device. > - """ > - self.pci_address =3D pci_address_line.strip().split(": ")[1].str= ip() > - > - def __str__(self) -> str: > - """The PCI address captures what the device is.""" > - return self.pci_address > - > - > -class VLANOffloadFlag(Flag): > - """Flag representing the VLAN offload settings of a NIC port.""" > - > - #: > - STRIP =3D auto() > - #: > - FILTER =3D auto() > - #: > - EXTEND =3D auto() > - #: > - QINQ_STRIP =3D auto() > - > - @classmethod > - def from_str_dict(cls, d): > - """Makes an instance from a dict containing the flag member name= s > with an "on" value. > - > - Args: > - d: A dictionary containing the flag members as keys and any > string value. > - > - Returns: > - A new instance of the flag. > - """ > - flag =3D cls(0) > - for name in cls.__members__: > - if d.get(name) =3D=3D "on": > - flag |=3D cls[name] > - return flag > - > - @classmethod > - def make_parser(cls) -> ParserFn: > - """Makes a parser function. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > - parser function that makes an instance of this flag from > text. > - """ > - return TextParser.wrap( > - TextParser.find( > - r"VLAN offload:\s+" > - r"strip (?Pon|off), " > - r"filter (?Pon|off), " > - r"extend (?Pon|off), " > - r"qinq strip (?Pon|off)", > - re.MULTILINE, > - named=3DTrue, > - ), > - cls.from_str_dict, > - ) > - > - > -class ChecksumOffloadOptions(Flag): > - """Flag representing checksum hardware offload layer options.""" > - > - #: > - ip =3D auto() > - #: > - udp =3D auto() > - #: > - tcp =3D auto() > - #: > - sctp =3D auto() > - #: > - outer_ip =3D auto() > - #: > - outer_udp =3D auto() > - > - > -class RSSOffloadTypesFlag(Flag): > - """Flag representing the RSS offload flow types supported by the NIC > port.""" > - > - #: > - ipv4 =3D auto() > - #: > - ipv4_frag =3D auto() > - #: > - ipv4_tcp =3D auto() > - #: > - ipv4_udp =3D auto() > - #: > - ipv4_sctp =3D auto() > - #: > - ipv4_other =3D auto() > - #: > - ipv6 =3D auto() > - #: > - ipv6_frag =3D auto() > - #: > - ipv6_tcp =3D auto() > - #: > - ipv6_udp =3D auto() > - #: > - ipv6_sctp =3D auto() > - #: > - ipv6_other =3D auto() > - #: > - l2_payload =3D auto() > - #: > - ipv6_ex =3D auto() > - #: > - ipv6_tcp_ex =3D auto() > - #: > - ipv6_udp_ex =3D auto() > - #: > - port =3D auto() > - #: > - vxlan =3D auto() > - #: > - geneve =3D auto() > - #: > - nvgre =3D auto() > - #: > - user_defined_22 =3D auto() > - #: > - gtpu =3D auto() > - #: > - eth =3D auto() > - #: > - s_vlan =3D auto() > - #: > - c_vlan =3D auto() > - #: > - esp =3D auto() > - #: > - ah =3D auto() > - #: > - l2tpv3 =3D auto() > - #: > - pfcp =3D auto() > - #: > - pppoe =3D auto() > - #: > - ecpri =3D auto() > - #: > - mpls =3D auto() > - #: > - ipv4_chksum =3D auto() > - #: > - l4_chksum =3D auto() > - #: > - l2tpv2 =3D auto() > - #: > - ipv6_flow_label =3D auto() > - #: > - user_defined_38 =3D auto() > - #: > - user_defined_39 =3D auto() > - #: > - user_defined_40 =3D auto() > - #: > - user_defined_41 =3D auto() > - #: > - user_defined_42 =3D auto() > - #: > - user_defined_43 =3D auto() > - #: > - user_defined_44 =3D auto() > - #: > - user_defined_45 =3D auto() > - #: > - user_defined_46 =3D auto() > - #: > - user_defined_47 =3D auto() > - #: > - user_defined_48 =3D auto() > - #: > - user_defined_49 =3D auto() > - #: > - user_defined_50 =3D auto() > - #: > - user_defined_51 =3D auto() > - #: > - l3_pre96 =3D auto() > - #: > - l3_pre64 =3D auto() > - #: > - l3_pre56 =3D auto() > - #: > - l3_pre48 =3D auto() > - #: > - l3_pre40 =3D auto() > - #: > - l3_pre32 =3D auto() > - #: > - l2_dst_only =3D auto() > - #: > - l2_src_only =3D auto() > - #: > - l4_dst_only =3D auto() > - #: > - l4_src_only =3D auto() > - #: > - l3_dst_only =3D auto() > - #: > - l3_src_only =3D auto() > - > - #: > - ip =3D ipv4 | ipv4_frag | ipv4_other | ipv6 | ipv6_frag | ipv6_other= | > ipv6_ex > - #: > - udp =3D ipv4_udp | ipv6_udp | ipv6_udp_ex > - #: > - tcp =3D ipv4_tcp | ipv6_tcp | ipv6_tcp_ex > - #: > - sctp =3D ipv4_sctp | ipv6_sctp > - #: > - tunnel =3D vxlan | geneve | nvgre > - #: > - vlan =3D s_vlan | c_vlan > - #: > - all =3D ( > - eth > - | vlan > - | ip > - | tcp > - | udp > - | sctp > - | l2_payload > - | l2tpv3 > - | esp > - | ah > - | pfcp > - | gtpu > - | ecpri > - | mpls > - | l2tpv2 > - ) > - > - @classmethod > - def from_list_string(cls, names: str) -> Self: > - """Makes a flag from a whitespace-separated list of names. > - > - Args: > - names: a whitespace-separated list containing the members of > this flag. > - > - Returns: > - An instance of this flag. > - """ > - flag =3D cls(0) > - for name in names.split(): > - flag |=3D cls.from_str(name) > - return flag > - > - @classmethod > - def from_str(cls, name: str) -> Self: > - """Makes a flag matching the supplied name. > - > - Args: > - name: a valid member of this flag in text > - Returns: > - An instance of this flag. > - """ > - member_name =3D name.strip().replace("-", "_") > - return cls[member_name] > - > - @classmethod > - def make_parser(cls) -> ParserFn: > - """Makes a parser function. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > - parser function that makes an instance of this flag from > text. > - """ > - return TextParser.wrap( > - TextParser.find(r"Supported RSS offload flow > types:((?:\r?\n? \S+)+)", re.MULTILINE), > - RSSOffloadTypesFlag.from_list_string, > - ) > - > - > -class DeviceCapabilitiesFlag(Flag): > - """Flag representing the device capabilities.""" > - > - #: Device supports Rx queue setup after device started. > - RUNTIME_RX_QUEUE_SETUP =3D auto() > - #: Device supports Tx queue setup after device started. > - RUNTIME_TX_QUEUE_SETUP =3D auto() > - #: Device supports shared Rx queue among ports within Rx domain and > switch domain. > - RXQ_SHARE =3D auto() > - #: Device supports keeping flow rules across restart. > - FLOW_RULE_KEEP =3D auto() > - #: Device supports keeping shared flow objects across restart. > - FLOW_SHARED_OBJECT_KEEP =3D auto() > - > - @classmethod > - def make_parser(cls) -> ParserFn: > - """Makes a parser function. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > - parser function that makes an instance of this flag from > text. > - """ > - return TextParser.wrap( > - TextParser.find_int(r"Device capabilities: (0x[A-Fa-f\d]+)")= , > - cls, > - ) > - > - > -class DeviceErrorHandlingMode(StrEnum): > - """Enum representing the device error handling mode.""" > - > - #: > - none =3D auto() > - #: > - passive =3D auto() > - #: > - proactive =3D auto() > - #: > - unknown =3D auto() > - > - @classmethod > - def make_parser(cls) -> ParserFn: > - """Makes a parser function. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > - parser function that makes an instance of this enum from > text. > - """ > - return TextParser.wrap(TextParser.find(r"Device error handling > mode: (\w+)"), cls) > - > - > -def make_device_private_info_parser() -> ParserFn: > - """Device private information parser. > - > - Ensures that we are not parsing invalid device private info output. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a parser > - function that parses the device private info from the TestPm= d > port info output. > - """ > - > - def _validate(info: str): > - info =3D info.strip() > - if info =3D=3D "none" or info.startswith("Invalid file") or > info.startswith("Failed to dump"): > - return None > - return info > - > - return TextParser.wrap(TextParser.find(r"Device private > info:\s+([\s\S]+)"), _validate) > - > - > -class RxQueueState(StrEnum): > - """RX queue states. > - > - References: > - DPDK lib: ``lib/ethdev/rte_ethdev.h`` > - testpmd display function: > ``app/test-pmd/config.c:get_queue_state_name()`` > - """ > - > - #: > - stopped =3D auto() > - #: > - started =3D auto() > - #: > - hairpin =3D auto() > - #: > - unknown =3D auto() > - > - @classmethod > - def make_parser(cls) -> ParserFn: > - """Makes a parser function. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > - parser function that makes an instance of this enum from > text. > - """ > - return TextParser.wrap(TextParser.find(r"Rx queue state: > ([^\r\n]+)"), cls) > - > - > -@dataclass > -class TestPmdQueueInfo(TextParser): > - """Dataclass representation of the common parts of the testpmd `show > rxq/txq info` commands.""" > - > - #: > - prefetch_threshold: int =3D > field(metadata=3DTextParser.find_int(r"prefetch threshold: (\d+)")) > - #: > - host_threshold: int =3D field(metadata=3DTextParser.find_int(r"host > threshold: (\d+)")) > - #: > - writeback_threshold: int =3D > field(metadata=3DTextParser.find_int(r"writeback threshold: (\d+)")) > - #: > - free_threshold: int =3D field(metadata=3DTextParser.find_int(r"free > threshold: (\d+)")) > - #: > - deferred_start: bool =3D field(metadata=3DTextParser.find("deferred > start: on")) > - #: The number of RXD/TXDs is just the ring size of the queue. > - ring_size: int =3D field(metadata=3DTextParser.find_int(r"Number of > (?:RXDs|TXDs): (\d+)")) > - #: > - is_queue_started: bool =3D field(metadata=3DTextParser.find("queue s= tate: > started")) > - #: > - burst_mode: str | None =3D field( > - default=3DNone, metadata=3DTextParser.find(r"Burst mode: ([^\r\n= ]+)") > - ) > - > - > -@dataclass > -class TestPmdTxqInfo(TestPmdQueueInfo): > - """Representation of testpmd's ``show txq info `= ` > command. > - > - References: > - testpmd command function: > ``app/test-pmd/cmdline.c:cmd_showqueue()`` > - testpmd display function: > ``app/test-pmd/config.c:rx_queue_infos_display()`` > - """ > - > - #: Ring size threshold > - rs_threshold: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"TX RS threshold= : > (\d+)\b") > - ) > - > - > -@dataclass > -class TestPmdRxqInfo(TestPmdQueueInfo): > - """Representation of testpmd's ``show rxq info `= ` > command. > - > - References: > - testpmd command function: > ``app/test-pmd/cmdline.c:cmd_showqueue()`` > - testpmd display function: > ``app/test-pmd/config.c:rx_queue_infos_display()`` > - """ > - > - #: Mempool used by that queue > - mempool: str | None =3D field(default=3DNone, > metadata=3DTextParser.find(r"Mempool: ([^\r\n]+)")) > - #: Drop packets if no descriptors are available > - drop_packets: bool | None =3D field( > - default=3DNone, metadata=3DTextParser.find(r"RX drop packets: on= ") > - ) > - #: Scattered packets Rx enabled > - scattered_packets: bool | None =3D field( > - default=3DNone, metadata=3DTextParser.find(r"RX scattered packet= s: > on") > - ) > - #: The state of the queue > - queue_state: str | None =3D field(default=3DNone, > metadata=3DRxQueueState.make_parser()) > - > - > -@dataclass > -class TestPmdPort(TextParser): > - """Dataclass representing the result of testpmd's ``show port info`` > command.""" > - > - #: > - id: int =3D field(metadata=3DTextParser.find_int(r"Infos for port > (\d+)\b")) > - #: > - device_name: str =3D field(metadata=3DTextParser.find(r"Device name: > ([^\r\n]+)")) > - #: > - driver_name: str =3D field(metadata=3DTextParser.find(r"Driver name: > ([^\r\n]+)")) > - #: > - socket_id: int =3D field(metadata=3DTextParser.find_int(r"Connect to > socket: (\d+)")) > - #: > - is_link_up: bool =3D field(metadata=3DTextParser.find("Link status: = up")) > - #: > - link_speed: str =3D field(metadata=3DTextParser.find(r"Link speed: > ([^\r\n]+)")) > - #: > - is_link_full_duplex: bool =3D field(metadata=3DTextParser.find("Link > duplex: full-duplex")) > - #: > - is_link_autonegotiated: bool =3D > field(metadata=3DTextParser.find("Autoneg status: On")) > - #: > - is_promiscuous_mode_enabled: bool =3D > field(metadata=3DTextParser.find("Promiscuous mode: enabled")) > - #: > - is_allmulticast_mode_enabled: bool =3D field( > - metadata=3DTextParser.find("Allmulticast mode: enabled") > - ) > - #: Maximum number of MAC addresses > - max_mac_addresses_num: int =3D field( > - metadata=3DTextParser.find_int(r"Maximum number of MAC addresses= : > (\d+)") > - ) > - #: Maximum configurable length of RX packet > - max_hash_mac_addresses_num: int =3D field( > - metadata=3DTextParser.find_int(r"Maximum number of MAC addresses= of > hash filtering: (\d+)") > - ) > - #: Minimum size of RX buffer > - min_rx_bufsize: int =3D field(metadata=3DTextParser.find_int(r"Minim= um > size of RX buffer: (\d+)")) > - #: Maximum configurable length of RX packet > - max_rx_packet_length: int =3D field( > - metadata=3DTextParser.find_int(r"Maximum configurable length of = RX > packet: (\d+)") > - ) > - #: Maximum configurable size of LRO aggregated packet > - max_lro_packet_size: int =3D field( > - metadata=3DTextParser.find_int(r"Maximum configurable size of LR= O > aggregated packet: (\d+)") > - ) > - > - #: Current number of RX queues > - rx_queues_num: int =3D field(metadata=3DTextParser.find_int(r"Curren= t > number of RX queues: (\d+)")) > - #: Max possible RX queues > - max_rx_queues_num: int =3D field(metadata=3DTextParser.find_int(r"Ma= x > possible RX queues: (\d+)")) > - #: Max possible number of RXDs per queue > - max_queue_rxd_num: int =3D field( > - metadata=3DTextParser.find_int(r"Max possible number of RXDs per > queue: (\d+)") > - ) > - #: Min possible number of RXDs per queue > - min_queue_rxd_num: int =3D field( > - metadata=3DTextParser.find_int(r"Min possible number of RXDs per > queue: (\d+)") > - ) > - #: RXDs number alignment > - rxd_alignment_num: int =3D field(metadata=3DTextParser.find_int(r"RX= Ds > number alignment: (\d+)")) > - > - #: Current number of TX queues > - tx_queues_num: int =3D field(metadata=3DTextParser.find_int(r"Curren= t > number of TX queues: (\d+)")) > - #: Max possible TX queues > - max_tx_queues_num: int =3D field(metadata=3DTextParser.find_int(r"Ma= x > possible TX queues: (\d+)")) > - #: Max possible number of TXDs per queue > - max_queue_txd_num: int =3D field( > - metadata=3DTextParser.find_int(r"Max possible number of TXDs per > queue: (\d+)") > - ) > - #: Min possible number of TXDs per queue > - min_queue_txd_num: int =3D field( > - metadata=3DTextParser.find_int(r"Min possible number of TXDs per > queue: (\d+)") > - ) > - #: TXDs number alignment > - txd_alignment_num: int =3D field(metadata=3DTextParser.find_int(r"TX= Ds > number alignment: (\d+)")) > - #: Max segment number per packet > - max_packet_segment_num: int =3D field( > - metadata=3DTextParser.find_int(r"Max segment number per packet: > (\d+)") > - ) > - #: Max segment number per MTU/TSO > - max_mtu_segment_num: int =3D field( > - metadata=3DTextParser.find_int(r"Max segment number per MTU\/TSO= : > (\d+)") > - ) > - > - #: > - device_capabilities: DeviceCapabilitiesFlag =3D field( > - metadata=3DDeviceCapabilitiesFlag.make_parser(), > - ) > - #: > - device_error_handling_mode: DeviceErrorHandlingMode | None =3D field= ( > - default=3DNone, metadata=3DDeviceErrorHandlingMode.make_parser() > - ) > - #: > - device_private_info: str | None =3D field( > - default=3DNone, > - metadata=3Dmake_device_private_info_parser(), > - ) > - > - #: > - hash_key_size: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"Hash key size i= n > bytes: (\d+)") > - ) > - #: > - redirection_table_size: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"Redirection tab= le > size: (\d+)") > - ) > - #: > - supported_rss_offload_flow_types: RSSOffloadTypesFlag =3D field( > - default=3DRSSOffloadTypesFlag(0), > metadata=3DRSSOffloadTypesFlag.make_parser() > - ) > - > - #: > - mac_address: str | None =3D field( > - default=3DNone, metadata=3DTextParser.find(r"MAC address: > ([A-Fa-f0-9:]+)") > - ) > - #: > - fw_version: str | None =3D field( > - default=3DNone, metadata=3DTextParser.find(r"Firmware-version: > ([^\r\n]+)") > - ) > - #: > - dev_args: str | None =3D field(default=3DNone, > metadata=3DTextParser.find(r"Devargs: ([^\r\n]+)")) > - #: Socket id of the memory allocation > - mem_alloc_socket_id: int | None =3D field( > - default=3DNone, > - metadata=3DTextParser.find_int(r"memory allocation on the socket= : > (\d+)"), > - ) > - #: > - mtu: int | None =3D field(default=3DNone, > metadata=3DTextParser.find_int(r"MTU: (\d+)")) > - > - #: > - vlan_offload: VLANOffloadFlag | None =3D field( > - default=3DNone, > - metadata=3DVLANOffloadFlag.make_parser(), > - ) > - > - #: Maximum size of RX buffer > - max_rx_bufsize: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"Maximum size of= RX > buffer: (\d+)") > - ) > - #: Maximum number of VFs > - max_vfs_num: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"Maximum number = of > VFs: (\d+)") > - ) > - #: Maximum number of VMDq pools > - max_vmdq_pools_num: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"Maximum number = of > VMDq pools: (\d+)") > - ) > - > - #: > - switch_name: str | None =3D field( > - default=3DNone, metadata=3DTextParser.find(r"Switch name: ([\r\n= ]+)") > - ) > - #: > - switch_domain_id: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"Switch domain I= d: > (\d+)") > - ) > - #: > - switch_port_id: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"Switch Port Id: > (\d+)") > - ) > - #: > - switch_rx_domain: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"Switch Rx domai= n: > (\d+)") > - ) > - > - > -@dataclass > -class TestPmdPortStats(TextParser): > - """Port statistics.""" > - > - #: > - port_id: int =3D field(metadata=3DTextParser.find_int(r"NIC statisti= cs > for port (\d+)")) > - > - #: > - rx_packets: int =3D > field(metadata=3DTextParser.find_int(r"RX-packets:\s+(\d+)")) > - #: > - rx_missed: int =3D > field(metadata=3DTextParser.find_int(r"RX-missed:\s+(\d+)")) > - #: > - rx_bytes: int =3D > field(metadata=3DTextParser.find_int(r"RX-bytes:\s+(\d+)")) > - #: > - rx_errors: int =3D > field(metadata=3DTextParser.find_int(r"RX-errors:\s+(\d+)")) > - #: > - rx_nombuf: int =3D > field(metadata=3DTextParser.find_int(r"RX-nombuf:\s+(\d+)")) > - > - #: > - tx_packets: int =3D > field(metadata=3DTextParser.find_int(r"TX-packets:\s+(\d+)")) > - #: > - tx_errors: int =3D > field(metadata=3DTextParser.find_int(r"TX-errors:\s+(\d+)")) > - #: > - tx_bytes: int =3D > field(metadata=3DTextParser.find_int(r"TX-bytes:\s+(\d+)")) > - > - #: > - rx_pps: int =3D field(metadata=3DTextParser.find_int(r"Rx-pps:\s+(\d= +)")) > - #: > - rx_bps: int =3D field(metadata=3DTextParser.find_int(r"Rx-bps:\s+(\d= +)")) > - > - #: > - tx_pps: int =3D field(metadata=3DTextParser.find_int(r"Tx-pps:\s+(\d= +)")) > - #: > - tx_bps: int =3D field(metadata=3DTextParser.find_int(r"Tx-bps:\s+(\d= +)")) > - > - > -@dataclass(kw_only=3DTrue) > -class FlowRule: > - """Class representation of flow rule parameters. > - > - This class represents the parameters of any flow rule as per the > - following pattern: > - > - [group {group_id}] [priority {level}] [ingress] [egress] > - [user_id {user_id}] pattern {item} [/ {item} [...]] / end > - actions {action} [/ {action} [...]] / end > - """ > - > - #: > - group_id: int | None =3D None > - #: > - priority_level: int | None =3D None > - #: > - direction: Literal["ingress", "egress"] > - #: > - user_id: int | None =3D None > - #: > - pattern: list[str] > - #: > - actions: list[str] > - > - def __str__(self) -> str: > - """Returns the string representation of this instance.""" > - ret =3D "" > - pattern =3D " / ".join(self.pattern) > - action =3D " / ".join(self.actions) > - if self.group_id is not None: > - ret +=3D f"group {self.group_id} " > - if self.priority_level is not None: > - ret +=3D f"priority {self.priority_level} " > - ret +=3D f"{self.direction} " > - if self.user_id is not None: > - ret +=3D f"user_id {self.user_id} " > - ret +=3D f"pattern {pattern} / end " > - ret +=3D f"actions {action} / end" > - return ret > - > - > -class PacketOffloadFlag(Flag): > - """Flag representing the Packet Offload Features Flags in DPDK. > - > - Values in this class are taken from the definitions in the RTE MBUF > core library in DPDK > - located in ``lib/mbuf/rte_mbuf_core.h``. It is expected that flag > values in this class will > - match the values they are set to in said DPDK library with one > exception; all values must be > - unique. For example, the definitions for unknown checksum flags in > ``rte_mbuf_core.h`` are all > - set to :data:`0`, but it is valuable to distinguish between them in > this framework. For this > - reason flags that are not unique in the DPDK library are set either > to values within the > - RTE_MBUF_F_FIRST_FREE-RTE_MBUF_F_LAST_FREE range for Rx or shifted > 61+ bits for Tx. > - > - References: > - DPDK lib: ``lib/mbuf/rte_mbuf_core.h`` > - """ > - > - # RX flags > - > - #: The RX packet is a 802.1q VLAN packet, and the tci has been saved > in mbuf->vlan_tci. If the > - #: flag RTE_MBUF_F_RX_VLAN_STRIPPED is also present, the VLAN header > has been stripped from > - #: mbuf data, else it is still present. > - RTE_MBUF_F_RX_VLAN =3D auto() > - > - #: RX packet with RSS hash result. > - RTE_MBUF_F_RX_RSS_HASH =3D auto() > - > - #: RX packet with FDIR match indicate. > - RTE_MBUF_F_RX_FDIR =3D auto() > - > - #: This flag is set when the outermost IP header checksum is detecte= d > as wrong by the hardware. > - RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD =3D 1 << 5 > - > - #: A vlan has been stripped by the hardware and its tci is saved in > mbuf->vlan_tci. This can > - #: only happen if vlan stripping is enabled in the RX configuration > of the PMD. When > - #: RTE_MBUF_F_RX_VLAN_STRIPPED is set, RTE_MBUF_F_RX_VLAN must also > be set. > - RTE_MBUF_F_RX_VLAN_STRIPPED =3D auto() > - > - #: No information about the RX IP checksum. Value is 0 in the DPDK > library. > - RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN =3D 1 << 23 > - #: The IP checksum in the packet is wrong. > - RTE_MBUF_F_RX_IP_CKSUM_BAD =3D 1 << 4 > - #: The IP checksum in the packet is valid. > - RTE_MBUF_F_RX_IP_CKSUM_GOOD =3D 1 << 7 > - #: The IP checksum is not correct in the packet data, but the > integrity of the IP header is > - #: verified. Value is RTE_MBUF_F_RX_IP_CKSUM_BAD | > RTE_MBUF_F_RX_IP_CKSUM_GOOD in the DPDK > - #: library. > - RTE_MBUF_F_RX_IP_CKSUM_NONE =3D 1 << 24 > - > - #: No information about the RX L4 checksum. Value is 0 in the DPDK > library. > - RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN =3D 1 << 25 > - #: The L4 checksum in the packet is wrong. > - RTE_MBUF_F_RX_L4_CKSUM_BAD =3D 1 << 3 > - #: The L4 checksum in the packet is valid. > - RTE_MBUF_F_RX_L4_CKSUM_GOOD =3D 1 << 8 > - #: The L4 checksum is not correct in the packet data, but the > integrity of the L4 data is > - #: verified. Value is RTE_MBUF_F_RX_L4_CKSUM_BAD | > RTE_MBUF_F_RX_L4_CKSUM_GOOD in the DPDK > - #: library. > - RTE_MBUF_F_RX_L4_CKSUM_NONE =3D 1 << 26 > - > - #: RX IEEE1588 L2 Ethernet PT Packet. > - RTE_MBUF_F_RX_IEEE1588_PTP =3D 1 << 9 > - #: RX IEEE1588 L2/L4 timestamped packet. > - RTE_MBUF_F_RX_IEEE1588_TMST =3D 1 << 10 > - > - #: FD id reported if FDIR match. > - RTE_MBUF_F_RX_FDIR_ID =3D 1 << 13 > - #: Flexible bytes reported if FDIR match. > - RTE_MBUF_F_RX_FDIR_FLX =3D 1 << 14 > - > - #: If both RTE_MBUF_F_RX_QINQ_STRIPPED and > RTE_MBUF_F_RX_VLAN_STRIPPED are set, the 2 VLANs > - #: have been stripped by the hardware. If RTE_MBUF_F_RX_QINQ_STRIPPE= D > is set and > - #: RTE_MBUF_F_RX_VLAN_STRIPPED is unset, only the outer VLAN is > removed from packet data. > - RTE_MBUF_F_RX_QINQ_STRIPPED =3D auto() > - > - #: When packets are coalesced by a hardware or virtual driver, this > flag can be set in the RX > - #: mbuf, meaning that the m->tso_segsz field is valid and is set to > the segment size of > - #: original packets. > - RTE_MBUF_F_RX_LRO =3D auto() > - > - #: Indicate that security offload processing was applied on the RX > packet. > - RTE_MBUF_F_RX_SEC_OFFLOAD =3D 1 << 18 > - #: Indicate that security offload processing failed on the RX packet= . > - RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED =3D auto() > - > - #: The RX packet is a double VLAN. If this flag is set, > RTE_MBUF_F_RX_VLAN must also be set. If > - #: the flag RTE_MBUF_F_RX_QINQ_STRIPPED is also present, both VLANs > headers have been stripped > - #: from mbuf data, else they are still present. > - RTE_MBUF_F_RX_QINQ =3D auto() > - > - #: No info about the outer RX L4 checksum. Value is 0 in the DPDK > library. > - RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN =3D 1 << 27 > - #: The outer L4 checksum in the packet is wrong > - RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD =3D 1 << 21 > - #: The outer L4 checksum in the packet is valid > - RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD =3D 1 << 22 > - #: Invalid outer L4 checksum state. Value is > - #: RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD | > RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD in the DPDK library. > - RTE_MBUF_F_RX_OUTER_L4_CKSUM_INVALID =3D 1 << 28 > - > - # TX flags > - > - #: Outer UDP checksum offload flag. This flag is used for enabling > outer UDP checksum in PMD. > - #: To use outer UDP checksum, the user either needs to enable the > following in mbuf: > - #: > - #: a) Fill outer_l2_len and outer_l3_len in mbuf. > - #: b) Set the RTE_MBUF_F_TX_OUTER_UDP_CKSUM flag. > - #: c) Set the RTE_MBUF_F_TX_OUTER_IPV4 or RTE_MBUF_F_TX_OUTER_IPV6 > flag. > - #: > - #: Or configure RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM offload flag. > - RTE_MBUF_F_TX_OUTER_UDP_CKSUM =3D 1 << 41 > - > - #: UDP Fragmentation Offload flag. This flag is used for enabling UD= P > fragmentation in SW or in > - #: HW. > - RTE_MBUF_F_TX_UDP_SEG =3D auto() > - > - #: Request security offload processing on the TX packet. To use Tx > security offload, the user > - #: needs to fill l2_len in mbuf indicating L2 header size and where > L3 header starts. > - #: Similarly, l3_len should also be filled along with ol_flags > reflecting current L3 type. > - RTE_MBUF_F_TX_SEC_OFFLOAD =3D auto() > - > - #: Offload the MACsec. This flag must be set by the application to > enable this offload feature > - #: for a packet to be transmitted. > - RTE_MBUF_F_TX_MACSEC =3D auto() > - > - # Bits 45:48 are used for the tunnel type in > ``lib/mbuf/rte_mbuf_core.h``, but some are modified > - # in this Flag to maintain uniqueness. The tunnel type must be > specified for TSO or checksum on > - # the inner part of tunnel packets. These flags can be used with > RTE_MBUF_F_TX_TCP_SEG for TSO, > - # or RTE_MBUF_F_TX_xxx_CKSUM. The mbuf fields for inner and outer > header lengths are required: > - # outer_l2_len, outer_l3_len, l2_len, l3_len, l4_len and tso_segsz > for TSO. > - > - #: > - RTE_MBUF_F_TX_TUNNEL_VXLAN =3D 1 << 45 > - #: > - RTE_MBUF_F_TX_TUNNEL_GRE =3D 1 << 46 > - #: Value is 3 << 45 in the DPDK library. > - RTE_MBUF_F_TX_TUNNEL_IPIP =3D 1 << 61 > - #: > - RTE_MBUF_F_TX_TUNNEL_GENEVE =3D 1 << 47 > - #: TX packet with MPLS-in-UDP RFC 7510 header. Value is 5 << 45 in > the DPDK library. > - RTE_MBUF_F_TX_TUNNEL_MPLSINUDP =3D 1 << 62 > - #: Value is 6 << 45 in the DPDK library. > - RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE =3D 1 << 63 > - #: Value is 7 << 45 in the DPDK library. > - RTE_MBUF_F_TX_TUNNEL_GTP =3D 1 << 64 > - #: > - RTE_MBUF_F_TX_TUNNEL_ESP =3D 1 << 48 > - #: Generic IP encapsulated tunnel type, used for TSO and checksum > offload. This can be used for > - #: tunnels which are not standards or listed above. It is preferred > to use specific tunnel > - #: flags like RTE_MBUF_F_TX_TUNNEL_GRE or RTE_MBUF_F_TX_TUNNEL_IPIP > if possible. The ethdev > - #: must be configured with RTE_ETH_TX_OFFLOAD_IP_TNL_TSO. Outer and > inner checksums are done > - #: according to the existing flags like RTE_MBUF_F_TX_xxx_CKSUM. > Specific tunnel headers that > - #: contain payload length, sequence id or checksum are not expected > to be updated. Value is > - #: 0xD << 45 in the DPDK library. > - RTE_MBUF_F_TX_TUNNEL_IP =3D 1 << 65 > - #: Generic UDP encapsulated tunnel type, used for TSO and checksum > offload. UDP tunnel type > - #: implies outer IP layer. It can be used for tunnels which are not > standards or listed above. > - #: It is preferred to use specific tunnel flags like > RTE_MBUF_F_TX_TUNNEL_VXLAN if possible. > - #: The ethdev must be configured with RTE_ETH_TX_OFFLOAD_UDP_TNL_TSO= . > Outer and inner checksums > - #: are done according to the existing flags like > RTE_MBUF_F_TX_xxx_CKSUM. Specific tunnel > - #: headers that contain payload length, sequence id or checksum are > not expected to be updated. > - #: value is 0xE << 45 in the DPDK library. > - RTE_MBUF_F_TX_TUNNEL_UDP =3D 1 << 66 > - > - #: Double VLAN insertion (QinQ) request to driver, driver may offloa= d > the insertion based on > - #: device capability. Mbuf 'vlan_tci' & 'vlan_tci_outer' must be > valid when this flag is set. > - RTE_MBUF_F_TX_QINQ =3D 1 << 49 > - > - #: TCP segmentation offload. To enable this offload feature for a > packet to be transmitted on > - #: hardware supporting TSO: > - #: > - #: - set the RTE_MBUF_F_TX_TCP_SEG flag in mbuf->ol_flags (this fla= g > implies > - #: RTE_MBUF_F_TX_TCP_CKSUM) > - #: - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6 > - #: * if it's IPv4, set the RTE_MBUF_F_TX_IP_CKSUM flag > - #: - fill the mbuf offload information: l2_len, l3_len, l4_len, > tso_segsz > - RTE_MBUF_F_TX_TCP_SEG =3D auto() > - > - #: TX IEEE1588 packet to timestamp. > - RTE_MBUF_F_TX_IEEE1588_TMST =3D auto() > - > - # Bits 52+53 used for L4 packet type with checksum enabled in > ``lib/mbuf/rte_mbuf_core.h`` but > - # some values must be modified in this framework to maintain > uniqueness. To use hardware > - # L4 checksum offload, the user needs to: > - # > - # - fill l2_len and l3_len in mbuf > - # - set the flags RTE_MBUF_F_TX_TCP_CKSUM, RTE_MBUF_F_TX_SCTP_CKSUM = or > - # RTE_MBUF_F_TX_UDP_CKSUM > - # - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6 > - > - #: Disable L4 cksum of TX pkt. Value is 0 in the DPDK library. > - RTE_MBUF_F_TX_L4_NO_CKSUM =3D 1 << 67 > - #: TCP cksum of TX pkt. Computed by NIC. > - RTE_MBUF_F_TX_TCP_CKSUM =3D 1 << 52 > - #: SCTP cksum of TX pkt. Computed by NIC. > - RTE_MBUF_F_TX_SCTP_CKSUM =3D 1 << 53 > - #: UDP cksum of TX pkt. Computed by NIC. Value is 3 << 52 in the DPD= K > library. > - RTE_MBUF_F_TX_UDP_CKSUM =3D 1 << 68 > - > - #: Offload the IP checksum in the hardware. The flag > RTE_MBUF_F_TX_IPV4 should also be set by > - #: the application, although a PMD will only check > RTE_MBUF_F_TX_IP_CKSUM. > - RTE_MBUF_F_TX_IP_CKSUM =3D 1 << 54 > - > - #: Packet is IPv4. This flag must be set when using any offload > feature (TSO, L3 or L4 > - #: checksum) to tell the NIC that the packet is an IPv4 packet. If > the packet is a tunneled > - #: packet, this flag is related to the inner headers. > - RTE_MBUF_F_TX_IPV4 =3D auto() > - #: Packet is IPv6. This flag must be set when using an offload > feature (TSO or L4 checksum) to > - #: tell the NIC that the packet is an IPv6 packet. If the packet is = a > tunneled packet, this > - #: flag is related to the inner headers. > - RTE_MBUF_F_TX_IPV6 =3D auto() > - #: VLAN tag insertion request to driver, driver may offload the > insertion based on the device > - #: capability. mbuf 'vlan_tci' field must be valid when this flag is > set. > - RTE_MBUF_F_TX_VLAN =3D auto() > - > - #: Offload the IP checksum of an external header in the hardware. Th= e > flag > - #: RTE_MBUF_F_TX_OUTER_IPV4 should also be set by the application, > although a PMD will only > - #: check RTE_MBUF_F_TX_OUTER_IP_CKSUM. > - RTE_MBUF_F_TX_OUTER_IP_CKSUM =3D auto() > - #: Packet outer header is IPv4. This flag must be set when using any > outer offload feature (L3 > - #: or L4 checksum) to tell the NIC that the outer header of the > tunneled packet is an IPv4 > - #: packet. > - RTE_MBUF_F_TX_OUTER_IPV4 =3D auto() > - #: Packet outer header is IPv6. This flag must be set when using any > outer offload feature (L4 > - #: checksum) to tell the NIC that the outer header of the tunneled > packet is an IPv6 packet. > - RTE_MBUF_F_TX_OUTER_IPV6 =3D auto() > - > - @classmethod > - def from_list_string(cls, names: str) -> Self: > - """Makes a flag from a whitespace-separated list of names. > - > - Args: > - names: a whitespace-separated list containing the members of > this flag. > - > - Returns: > - An instance of this flag. > - """ > - flag =3D cls(0) > - for name in names.split(): > - flag |=3D cls.from_str(name) > - return flag > - > - @classmethod > - def from_str(cls, name: str) -> Self: > - """Makes a flag matching the supplied name. > - > - Args: > - name: a valid member of this flag in text > - Returns: > - An instance of this flag. > - """ > - member_name =3D name.strip().replace("-", "_") > - return cls[member_name] > - > - @classmethod > - def make_parser(cls) -> ParserFn: > - """Makes a parser function. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > - parser function that makes an instance of this flag from > text. > - """ > - return TextParser.wrap( > - TextParser.find(r"ol_flags: ([^\n]+)"), > - cls.from_list_string, > - ) > - > - > -class RtePTypes(Flag): > - """Flag representing possible packet types in DPDK verbose output. > - > - Values in this class are derived from definitions in the RTE MBUF > ptype library in DPDK located > - in ``lib/mbuf/rte_mbuf_ptype.h``. Specifically, the names of values > in this class should match > - the possible return options from the functions > ``rte_get_ptype_*_name`` in ``rte_mbuf_ptype.c``. > - > - References: > - DPDK lib: ``lib/mbuf/rte_mbuf_ptype.h`` > - DPDK ptype name formatting functions: > ``lib/mbuf/rte_mbuf_ptype.c:rte_get_ptype_*_name()`` > - """ > - > - # L2 > - #: Ethernet packet type. This is used for outer packet for tunneling > cases. > - L2_ETHER =3D auto() > - #: Ethernet packet type for time sync. > - L2_ETHER_TIMESYNC =3D auto() > - #: ARP (Address Resolution Protocol) packet type. > - L2_ETHER_ARP =3D auto() > - #: LLDP (Link Layer Discovery Protocol) packet type. > - L2_ETHER_LLDP =3D auto() > - #: NSH (Network Service Header) packet type. > - L2_ETHER_NSH =3D auto() > - #: VLAN packet type. > - L2_ETHER_VLAN =3D auto() > - #: QinQ packet type. > - L2_ETHER_QINQ =3D auto() > - #: PPPOE packet type. > - L2_ETHER_PPPOE =3D auto() > - #: FCoE packet type.. > - L2_ETHER_FCOE =3D auto() > - #: MPLS packet type. > - L2_ETHER_MPLS =3D auto() > - #: No L2 packet information. > - L2_UNKNOWN =3D auto() > - > - # L3 > - #: IP (Internet Protocol) version 4 packet type. This is used for > outer packet for tunneling > - #: cases, and does not contain any header option. > - L3_IPV4 =3D auto() > - #: IP (Internet Protocol) version 4 packet type. This is used for > outer packet for tunneling > - #: cases, and contains header options. > - L3_IPV4_EXT =3D auto() > - #: IP (Internet Protocol) version 6 packet type. This is used for > outer packet for tunneling > - #: cases, and does not contain any extension header. > - L3_IPV6 =3D auto() > - #: IP (Internet Protocol) version 4 packet type. This is used for > outer packet for tunneling > - #: cases, and may or maynot contain header options. > - L3_IPV4_EXT_UNKNOWN =3D auto() > - #: IP (Internet Protocol) version 6 packet type. This is used for > outer packet for tunneling > - #: cases, and contains extension headers. > - L3_IPV6_EXT =3D auto() > - #: IP (Internet Protocol) version 6 packet type. This is used for > outer packet for tunneling > - #: cases, and may or maynot contain extension headers. > - L3_IPV6_EXT_UNKNOWN =3D auto() > - #: No L3 packet information. > - L3_UNKNOWN =3D auto() > - > - # L4 > - #: TCP (Transmission Control Protocol) packet type. This is used for > outer packet for tunneling > - #: cases. > - L4_TCP =3D auto() > - #: UDP (User Datagram Protocol) packet type. This is used for outer > packet for tunneling cases. > - L4_UDP =3D auto() > - #: Fragmented IP (Internet Protocol) packet type. This is used for > outer packet for tunneling > - #: cases and refers to those packets of any IP types which can be > recognized as fragmented. A > - #: fragmented packet cannot be recognized as any other L4 types > (RTE_PTYPE_L4_TCP, > - #: RTE_PTYPE_L4_UDP, RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP, > RTE_PTYPE_L4_NONFRAG). > - L4_FRAG =3D auto() > - #: SCTP (Stream Control Transmission Protocol) packet type. This is > used for outer packet for > - #: tunneling cases. > - L4_SCTP =3D auto() > - #: ICMP (Internet Control Message Protocol) packet type. This is use= d > for outer packet for > - #: tunneling cases. > - L4_ICMP =3D auto() > - #: Non-fragmented IP (Internet Protocol) packet type. This is used > for outer packet for > - #: tunneling cases and refers to those packets of any IP types, that > cannot be recognized as > - #: any of the above L4 types (RTE_PTYPE_L4_TCP, RTE_PTYPE_L4_UDP, > RTE_PTYPE_L4_FRAG, > - #: RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP). > - L4_NONFRAG =3D auto() > - #: IGMP (Internet Group Management Protocol) packet type. > - L4_IGMP =3D auto() > - #: No L4 packet information. > - L4_UNKNOWN =3D auto() > - > - # Tunnel > - #: IP (Internet Protocol) in IP (Internet Protocol) tunneling packet > type. > - TUNNEL_IP =3D auto() > - #: GRE (Generic Routing Encapsulation) tunneling packet type. > - TUNNEL_GRE =3D auto() > - #: VXLAN (Virtual eXtensible Local Area Network) tunneling packet > type. > - TUNNEL_VXLAN =3D auto() > - #: NVGRE (Network Virtualization using Generic Routing Encapsulation= ) > tunneling packet type. > - TUNNEL_NVGRE =3D auto() > - #: GENEVE (Generic Network Virtualization Encapsulation) tunneling > packet type. > - TUNNEL_GENEVE =3D auto() > - #: Tunneling packet type of Teredo, VXLAN (Virtual eXtensible Local > Area Network) or GRE > - #: (Generic Routing Encapsulation) could be recognized as this packe= t > type, if they can not be > - #: recognized independently as of hardware capability. > - TUNNEL_GRENAT =3D auto() > - #: GTP-C (GPRS Tunnelling Protocol) control tunneling packet type. > - TUNNEL_GTPC =3D auto() > - #: GTP-U (GPRS Tunnelling Protocol) user data tunneling packet type. > - TUNNEL_GTPU =3D auto() > - #: ESP (IP Encapsulating Security Payload) tunneling packet type. > - TUNNEL_ESP =3D auto() > - #: L2TP (Layer 2 Tunneling Protocol) tunneling packet type. > - TUNNEL_L2TP =3D auto() > - #: VXLAN-GPE (VXLAN Generic Protocol Extension) tunneling packet typ= e. > - TUNNEL_VXLAN_GPE =3D auto() > - #: MPLS-in-UDP tunneling packet type (RFC 7510). > - TUNNEL_MPLS_IN_UDP =3D auto() > - #: MPLS-in-GRE tunneling packet type (RFC 4023). > - TUNNEL_MPLS_IN_GRE =3D auto() > - #: No tunnel information found on the packet. > - TUNNEL_UNKNOWN =3D auto() > - > - # Inner L2 > - #: Ethernet packet type. This is used for inner packet type only. > - INNER_L2_ETHER =3D auto() > - #: Ethernet packet type with VLAN (Virtual Local Area Network) tag. > - INNER_L2_ETHER_VLAN =3D auto() > - #: QinQ packet type. > - INNER_L2_ETHER_QINQ =3D auto() > - #: No inner L2 information found on the packet. > - INNER_L2_UNKNOWN =3D auto() > - > - # Inner L3 > - #: IP (Internet Protocol) version 4 packet type. This is used for > inner packet only, and does > - #: not contain any header option. > - INNER_L3_IPV4 =3D auto() > - #: IP (Internet Protocol) version 4 packet type. This is used for > inner packet only, and > - #: contains header options. > - INNER_L3_IPV4_EXT =3D auto() > - #: IP (Internet Protocol) version 6 packet type. This is used for > inner packet only, and does > - #: not contain any extension header. > - INNER_L3_IPV6 =3D auto() > - #: IP (Internet Protocol) version 4 packet type. This is used for > inner packet only, and may or > - #: may not contain header options. > - INNER_L3_IPV4_EXT_UNKNOWN =3D auto() > - #: IP (Internet Protocol) version 6 packet type. This is used for > inner packet only, and > - #: contains extension headers. > - INNER_L3_IPV6_EXT =3D auto() > - #: IP (Internet Protocol) version 6 packet type. This is used for > inner packet only, and may or > - #: may not contain extension headers. > - INNER_L3_IPV6_EXT_UNKNOWN =3D auto() > - #: No inner L3 information found on the packet. > - INNER_L3_UNKNOWN =3D auto() > - > - # Inner L4 > - #: TCP (Transmission Control Protocol) packet type. This is used for > inner packet only. > - INNER_L4_TCP =3D auto() > - #: UDP (User Datagram Protocol) packet type. This is used for inner > packet only. > - INNER_L4_UDP =3D auto() > - #: Fragmented IP (Internet Protocol) packet type. This is used for > inner packet only, and may > - #: or maynot have a layer 4 packet. > - INNER_L4_FRAG =3D auto() > - #: SCTP (Stream Control Transmission Protocol) packet type. This is > used for inner packet only. > - INNER_L4_SCTP =3D auto() > - #: ICMP (Internet Control Message Protocol) packet type. This is use= d > for inner packet only. > - INNER_L4_ICMP =3D auto() > - #: Non-fragmented IP (Internet Protocol) packet type. It is used for > inner packet only, and may > - #: or may not have other unknown layer 4 packet types. > - INNER_L4_NONFRAG =3D auto() > - #: No inner L4 information found on the packet. > - INNER_L4_UNKNOWN =3D auto() > - > - @classmethod > - def from_list_string(cls, names: str) -> Self: > - """Makes a flag from a whitespace-separated list of names. > - > - Args: > - names: a whitespace-separated list containing the members of > this flag. > - > - Returns: > - An instance of this flag. > - """ > - flag =3D cls(0) > - for name in names.split(): > - flag |=3D cls.from_str(name) > - return flag > - > - @classmethod > - def from_str(cls, name: str) -> Self: > - """Makes a flag matching the supplied name. > - > - Args: > - name: a valid member of this flag in text > - Returns: > - An instance of this flag. > - """ > - member_name =3D name.strip().replace("-", "_") > - return cls[member_name] > - > - @classmethod > - def make_parser(cls, hw: bool) -> ParserFn: > - """Makes a parser function. > - > - Args: > - hw: Whether to make a parser for hardware ptypes or software > ptypes. If :data:`True`, > - hardware ptypes will be collected, otherwise software > pytpes will. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > - parser function that makes an instance of this flag from > text. > - """ > - return TextParser.wrap( > - TextParser.find(f"{'hw' if hw else 'sw'} ptype: ([^-]+)"), > - cls.from_list_string, > - ) > - > - > -@dataclass > -class TestPmdVerbosePacket(TextParser): > - """Packet information provided by verbose output in Testpmd. > - > - This dataclass expects that packet information be prepended with the > starting line of packet > - bursts. Specifically, the line that reads "port X/queue Y: > sent/received Z packets". > - """ > - > - #: ID of the port that handled the packet. > - port_id: int =3D field(metadata=3DTextParser.find_int(r"port (\d+)/q= ueue > \d+")) > - #: ID of the queue that handled the packet. > - queue_id: int =3D field(metadata=3DTextParser.find_int(r"port \d+/qu= eue > (\d+)")) > - #: Whether the packet was received or sent by the queue/port. > - was_received: bool =3D field(metadata=3DTextParser.find(r"received \= d+ > packets")) > - #: > - src_mac: str =3D > field(metadata=3DTextParser.find(f"src=3D({REGEX_FOR_MAC_ADDRESS})")) > - #: > - dst_mac: str =3D > field(metadata=3DTextParser.find(f"dst=3D({REGEX_FOR_MAC_ADDRESS})")) > - #: Memory pool the packet was handled on. > - pool: str =3D field(metadata=3DTextParser.find(r"pool=3D(\S+)")) > - #: Packet type in hex. > - p_type: int =3D > field(metadata=3DTextParser.find_int(r"type=3D(0x[a-fA-F\d]+)")) > - #: > - length: int =3D field(metadata=3DTextParser.find_int(r"length=3D(\d+= )")) > - #: Number of segments in the packet. > - nb_segs: int =3D field(metadata=3DTextParser.find_int(r"nb_segs=3D(\= d+)")) > - #: Hardware packet type. > - hw_ptype: RtePTypes =3D field(metadata=3DRtePTypes.make_parser(hw=3D= True)) > - #: Software packet type. > - sw_ptype: RtePTypes =3D field(metadata=3DRtePTypes.make_parser(hw=3D= False)) > - #: > - l2_len: int =3D field(metadata=3DTextParser.find_int(r"l2_len=3D(\d+= )")) > - #: > - ol_flags: PacketOffloadFlag =3D > field(metadata=3DPacketOffloadFlag.make_parser()) > - #: RSS hash of the packet in hex. > - rss_hash: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"RSS > hash=3D(0x[a-fA-F\d]+)") > - ) > - #: RSS queue that handled the packet in hex. > - rss_queue: int | None =3D field( > - default=3DNone, metadata=3DTextParser.find_int(r"RSS > queue=3D(0x[a-fA-F\d]+)") > - ) > - #: > - l3_len: int | None =3D field(default=3DNone, > metadata=3DTextParser.find_int(r"l3_len=3D(\d+)")) > - #: > - l4_len: int | None =3D field(default=3DNone, > metadata=3DTextParser.find_int(r"l4_len=3D(\d+)")) > - #: > - l4_dport: int | None =3D field( > - default=3DNone, > - metadata=3DTextParser.find_int(r"Destination (?:TCP|UDP) > port=3D(\d+)"), > - ) > - > - > -class RxOffloadCapability(Flag): > - """Rx offload capabilities of a device. > - > - The flags are taken from ``lib/ethdev/rte_ethdev.h``. > - They're prefixed with ``RTE_ETH_RX_OFFLOAD`` in > ``lib/ethdev/rte_ethdev.h`` > - instead of ``RX_OFFLOAD``, which is what testpmd changes the prefix > to. > - The values are not contiguous, so the correspondence is preserved > - by specifying concrete values interspersed between auto() values. > - > - The ``RX_OFFLOAD`` prefix has been preserved so that the same flag > names can be used > - in :class:`NicCapability`. The prefix is needed in > :class:`NicCapability` since there's > - no other qualifier which would sufficiently distinguish it from othe= r > capabilities. > - > - References: > - DPDK lib: ``lib/ethdev/rte_ethdev.h`` > - testpmd display function: > ``app/test-pmd/cmdline.c:print_rx_offloads()`` > - """ > - > - #: > - RX_OFFLOAD_VLAN_STRIP =3D auto() > - #: Device supports L3 checksum offload. > - RX_OFFLOAD_IPV4_CKSUM =3D auto() > - #: Device supports L4 checksum offload. > - RX_OFFLOAD_UDP_CKSUM =3D auto() > - #: Device supports L4 checksum offload. > - RX_OFFLOAD_TCP_CKSUM =3D auto() > - #: Device supports Large Receive Offload. > - RX_OFFLOAD_TCP_LRO =3D auto() > - #: Device supports QinQ (queue in queue) offload. > - RX_OFFLOAD_QINQ_STRIP =3D auto() > - #: Device supports inner packet L3 checksum. > - RX_OFFLOAD_OUTER_IPV4_CKSUM =3D auto() > - #: Device supports MACsec. > - RX_OFFLOAD_MACSEC_STRIP =3D auto() > - #: Device supports filtering of a VLAN Tag identifier. > - RX_OFFLOAD_VLAN_FILTER =3D 1 << 9 > - #: Device supports VLAN offload. > - RX_OFFLOAD_VLAN_EXTEND =3D auto() > - #: Device supports receiving segmented mbufs. > - RX_OFFLOAD_SCATTER =3D 1 << 13 > - #: Device supports Timestamp. > - RX_OFFLOAD_TIMESTAMP =3D auto() > - #: Device supports crypto processing while packet is received in NIC= . > - RX_OFFLOAD_SECURITY =3D auto() > - #: Device supports CRC stripping. > - RX_OFFLOAD_KEEP_CRC =3D auto() > - #: Device supports L4 checksum offload. > - RX_OFFLOAD_SCTP_CKSUM =3D auto() > - #: Device supports inner packet L4 checksum. > - RX_OFFLOAD_OUTER_UDP_CKSUM =3D auto() > - #: Device supports RSS hashing. > - RX_OFFLOAD_RSS_HASH =3D auto() > - #: Device supports > - RX_OFFLOAD_BUFFER_SPLIT =3D auto() > - #: Device supports all checksum capabilities. > - RX_OFFLOAD_CHECKSUM =3D RX_OFFLOAD_IPV4_CKSUM | RX_OFFLOAD_UDP_CKSUM= | > RX_OFFLOAD_TCP_CKSUM > - #: Device supports all VLAN capabilities. > - RX_OFFLOAD_VLAN =3D ( > - RX_OFFLOAD_VLAN_STRIP > - | RX_OFFLOAD_VLAN_FILTER > - | RX_OFFLOAD_VLAN_EXTEND > - | RX_OFFLOAD_QINQ_STRIP > - ) > - > - @classmethod > - def from_string(cls, line: str) -> Self: > - """Make an instance from a string containing the flag names > separated with a space. > - > - Args: > - line: The line to parse. > - > - Returns: > - A new instance containing all found flags. > - """ > - flag =3D cls(0) > - for flag_name in line.split(): > - flag |=3D cls[f"RX_OFFLOAD_{flag_name}"] > - return flag > - > - @classmethod > - def make_parser(cls, per_port: bool) -> ParserFn: > - """Make a parser function. > - > - Args: > - per_port: If :data:`True`, will return capabilities per port= . > If :data:`False`, > - will return capabilities per queue. > - > - Returns: > - ParserFn: A dictionary for the `dataclasses.field` metadata > argument containing a > - parser function that makes an instance of this flag from > text. > - """ > - granularity =3D "Port" if per_port else "Queue" > - return TextParser.wrap( > - TextParser.find(rf"Per {granularity}\s+:(.*)$", re.MULTILINE= ), > - cls.from_string, > - ) > - > - > -@dataclass > -class RxOffloadCapabilities(TextParser): > - """The result of testpmd's ``show port rx_offload > capabilities`` command. > - > - References: > - testpmd command function: > ``app/test-pmd/cmdline.c:cmd_rx_offload_get_capa()`` > - testpmd display function: > ``app/test-pmd/cmdline.c:cmd_rx_offload_get_capa_parsed()`` > - """ > - > - #: > - port_id: int =3D field( > - metadata=3DTextParser.find_int(r"Rx Offloading Capabilities of p= ort > (\d+) :") > - ) > - #: Per-queue Rx offload capabilities. > - per_queue: RxOffloadCapability =3D > field(metadata=3DRxOffloadCapability.make_parser(False)) > - #: Capabilities other than per-queue Rx offload capabilities. > - per_port: RxOffloadCapability =3D > field(metadata=3DRxOffloadCapability.make_parser(True)) > - > - > -@dataclass > -class TestPmdPortFlowCtrl(TextParser): > - """Class representing a port's flow control parameters. > - > - The parameters can also be parsed from the output of ``show port > flow_ctrl``. > - """ > - > - #: Enable Reactive Extensions. > - rx: bool =3D field(default=3DFalse, metadata=3DTextParser.find(r"Rx = pause: > on")) > - #: Enable Transmit. > - tx: bool =3D field(default=3DFalse, metadata=3DTextParser.find(r"Tx = pause: > on")) > - #: High threshold value to trigger XOFF. > - high_water: int =3D field( > - default=3D0, metadata=3DTextParser.find_int(r"High waterline: > (0x[a-fA-F\d]+)") > - ) > - #: Low threshold value to trigger XON. > - low_water: int =3D field( > - default=3D0, metadata=3DTextParser.find_int(r"Low waterline: > (0x[a-fA-F\d]+)") > - ) > - #: Pause quota in the Pause frame. > - pause_time: int =3D field(default=3D0, > metadata=3DTextParser.find_int(r"Pause time: (0x[a-fA-F\d]+)")) > - #: Send XON frame. > - send_xon: bool =3D field(default=3DFalse, metadata=3DTextParser.find= (r"Tx > pause: on")) > - #: Enable receiving MAC control frames. > - mac_ctrl_frame_fwd: bool =3D field(default=3DFalse, > metadata=3DTextParser.find(r"Tx pause: on")) > - #: Change the auto-negotiation parameter. > - autoneg: bool =3D field(default=3DFalse, > metadata=3DTextParser.find(r"Autoneg: on")) > - > - def __str__(self) -> str: > - """Returns the string representation of this instance.""" > - ret =3D ( > - f"rx {'on' if self.rx else 'off'} " > - f"tx {'on' if self.tx else 'off'} " > - f"{self.high_water} " > - f"{self.low_water} " > - f"{self.pause_time} " > - f"{1 if self.send_xon else 0} " > - f"mac_ctrl_frame_fwd {'on' if self.mac_ctrl_frame_fwd else > 'off'} " > - f"autoneg {'on' if self.autoneg else 'off'}" > - ) > - return ret > - > - > -def requires_stopped_ports(func: TestPmdShellMethod) -> > TestPmdShellMethod: > - """Decorator for :class:`TestPmdShell` commands methods that require > stopped ports. > - > - If the decorated method is called while the ports are started, then > these are stopped before > - continuing. > - > - Args: > - func: The :class:`TestPmdShell` method to decorate. > - """ > - > - @functools.wraps(func) > - def _wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs= ): > - if self.ports_started: > - self._logger.debug("Ports need to be stopped to continue.") > - self.stop_all_ports() > - > - return func(self, *args, **kwargs) > - > - return _wrapper > - > - > -def requires_started_ports(func: TestPmdShellMethod) -> > TestPmdShellMethod: > - """Decorator for :class:`TestPmdShell` commands methods that require > started ports. > - > - If the decorated method is called while the ports are stopped, then > these are started before > - continuing. > - > - Args: > - func: The :class:`TestPmdShell` method to decorate. > - """ > - > - @functools.wraps(func) > - def _wrapper(self: "TestPmdShell", *args: P.args, **kwargs: P.kwargs= ): > - if not self.ports_started: > - self._logger.debug("Ports need to be started to continue.") > - self.start_all_ports() > - > - return func(self, *args, **kwargs) > - > - return _wrapper > - > - > -def add_remove_mtu(mtu: int =3D 1500) -> Callable[[TestPmdShellMethod], > TestPmdShellMethod]: > - """Configure MTU to `mtu` on all ports, run the decorated function, > then revert. > - > - Args: > - mtu: The MTU to configure all ports on. > - > - Returns: > - The method decorated with setting and reverting MTU. > - """ > - > - def decorator(func: TestPmdShellMethod) -> TestPmdShellMethod: > - @functools.wraps(func) > - def wrapper(self: "TestPmdShell", *args: P.args, **kwargs: > P.kwargs): > - original_mtu =3D self.ports[0].mtu > - self.set_port_mtu_all(mtu=3Dmtu, verify=3DFalse) > - retval =3D func(self, *args, **kwargs) > - self.set_port_mtu_all(original_mtu if original_mtu else 1500= , > verify=3DFalse) > - return retval > - > - return wrapper > - > - return decorator > - > - > -class TestPmdShell(DPDKShell): > - """Testpmd interactive shell. > - > - The testpmd shell users should never use > - the :meth:`~.interactive_shell.InteractiveShell.send_command` method > directly, but rather > - call specialized methods. If there isn't one that satisfies a need, > it should be added. > - > - Attributes: > - ports_started: Indicates whether the ports are started. > - """ > - > - _app_params: TestPmdParams > - _ports: list[TestPmdPort] | None > - > - #: The testpmd's prompt. > - _default_prompt: ClassVar[str] =3D "testpmd>" > - > - #: This forces the prompt to appear after sending a command. > - _command_extra_chars: ClassVar[str] =3D "\n" > - > - ports_started: bool > - > - def __init__( > - self, > - name: str | None =3D None, > - privileged: bool =3D True, > - **app_params: Unpack[TestPmdParamsDict], > - ) -> None: > - """Overrides :meth:`~.dpdk_shell.DPDKShell.__init__`. Changes > app_params to kwargs.""" > - if "port_topology" not in app_params and get_ctx().topology.type > is TopologyType.one_link: > - app_params["port_topology"] =3D PortTopology.loop > - super().__init__(name, privileged, > app_params=3DTestPmdParams(**app_params)) > - self.ports_started =3D not self._app_params.disable_device_start > - self._ports =3D None > - > - @property > - def path(self) -> PurePath: > - """The path to the testpmd executable.""" > - return PurePath("app/dpdk-testpmd") > - > - @property > - def ports(self) -> list[TestPmdPort]: > - """The ports of the instance. > - > - This caches the ports returned by :meth:`show_port_info_all`. > - To force an update of port information, execute > :meth:`show_port_info_all` or > - :meth:`show_port_info`. > - > - Returns: The list of known testpmd ports. > - """ > - if self._ports is None: > - return self.show_port_info_all() > - return self._ports > - > - @requires_started_ports > - def start(self, verify: bool =3D True) -> None: > - """Start packet forwarding with the current configuration. > - > - Args: > - verify: If :data:`True` , a second start command will be sen= t > in an attempt to verify > - packet forwarding started as expected. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and forwarding fails to > - start or ports fail to come up. > - """ > - self.send_command("start") > - if verify: > - # If forwarding was already started, sending "start" again > should tell us > - start_cmd_output =3D self.send_command("start") > - if "Packet forwarding already started" not in > start_cmd_output: > - self._logger.debug(f"Failed to start packet forwarding: > \n{start_cmd_output}") > - raise InteractiveCommandExecutionError("Testpmd failed t= o > start packet forwarding.") > - > - def stop(self, verify: bool =3D True) -> str: > - """Stop packet forwarding. > - > - Args: > - verify: If :data:`True` , the output of the stop command is > scanned to verify that > - forwarding was stopped successfully or not started. If > neither is found, it is > - considered an error. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the command to stop > - forwarding results in an error. > - > - Returns: > - Output gathered from the stop command and all other precedin= g > logs in the buffer. This > - output is most often used to view forwarding statistics that > are displayed when this > - command is sent as well as any verbose packet information > that hasn't been consumed > - prior to calling this method. > - """ > - stop_cmd_output =3D self.send_command("stop") > - if verify: > - if ( > - "Done." not in stop_cmd_output > - and "Packet forwarding not started" not in stop_cmd_outp= ut > - ): > - self._logger.debug(f"Failed to stop packet forwarding: > \n{stop_cmd_output}") > - raise InteractiveCommandExecutionError("Testpmd failed t= o > stop packet forwarding.") > - return stop_cmd_output > - > - def get_devices(self) -> list[TestPmdDevice]: > - """Get a list of device names that are known to testpmd. > - > - Uses the device info listed in testpmd and then parses the outpu= t. > - > - Returns: > - A list of devices. > - """ > - dev_info: str =3D self.send_command("show device info all") > - dev_list: list[TestPmdDevice] =3D [] > - for line in dev_info.split("\n"): > - if "device name:" in line.lower(): > - dev_list.append(TestPmdDevice(line)) > - return dev_list > - > - def wait_link_status_up(self, port_id: int, timeout=3DSETTINGS.timeo= ut) > -> bool: > - """Wait until the link status on the given port is "up". > - > - Arguments: > - port_id: Port to check the link status on. > - timeout: Time to wait for the link to come up. The default > value for this > - argument may be modified using the :option:`--timeout` > command-line argument > - or the :envvar:`DTS_TIMEOUT` environment variable. > - > - Returns: > - Whether the link came up in time or not. > - """ > - time_to_stop =3D time.time() + timeout > - port_info: str =3D "" > - while time.time() < time_to_stop: > - port_info =3D self.send_command(f"show port info {port_id}") > - if "Link status: up" in port_info: > - break > - time.sleep(0.5) > - else: > - self._logger.error(f"The link for port {port_id} did not com= e > up in the given timeout.") > - return "Link status: up" in port_info > - > - def set_forward_mode(self, mode: SimpleForwardingModes, verify: bool > =3D True): > - """Set packet forwarding mode. > - > - Args: > - mode: The forwarding mode to use. > - verify: If :data:`True` the output of the command will be > scanned in an attempt to > - verify that the forwarding mode was set to `mode` > properly. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the forwarding mode > - fails to update. > - """ > - set_fwd_output =3D self.send_command(f"set fwd {mode.value}") > - if verify: > - if f"Set {mode.value} packet forwarding mode" not in > set_fwd_output: > - self._logger.debug(f"Failed to set fwd mode to > {mode.value}:\n{set_fwd_output}") > - raise InteractiveCommandExecutionError( > - f"Test pmd failed to set fwd mode to {mode.value}" > - ) > - > - def stop_all_ports(self, verify: bool =3D True) -> None: > - """Stops all the ports. > - > - Args: > - verify: If :data:`True`, the output of the command will be > checked for a successful > - execution. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the ports were not > - stopped successfully. > - """ > - self._logger.debug("Stopping all the ports...") > - output =3D self.send_command("port stop all") > - if verify and not output.strip().endswith("Done"): > - raise InteractiveCommandExecutionError("Ports were not > stopped successfully.") > - > - self.ports_started =3D False > - > - def start_all_ports(self, verify: bool =3D True) -> None: > - """Starts all the ports. > - > - Args: > - verify: If :data:`True`, the output of the command will be > checked for a successful > - execution. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the ports were not > - started successfully. > - """ > - self._logger.debug("Starting all the ports...") > - output =3D self.send_command("port start all") > - if verify and not output.strip().endswith("Done"): > - raise InteractiveCommandExecutionError("Ports were not > started successfully.") > - > - self.ports_started =3D True > - > - @requires_stopped_ports > - def set_ports_queues(self, number_of: int) -> None: > - """Sets the number of queues per port. > - > - Args: > - number_of: The number of RX/TX queues to create per port. > - > - Raises: > - InternalError: If `number_of` is invalid. > - """ > - if number_of < 1: > - raise InternalError("The number of queues must be positive > and non-zero.") > - > - self.send_command(f"port config all rxq {number_of}") > - self.send_command(f"port config all txq {number_of}") > - > - @requires_stopped_ports > - def close_all_ports(self, verify: bool =3D True) -> None: > - """Close all ports. > - > - Args: > - verify: If :data:`True` the output of the close command will > be scanned in an attempt > - to verify that all ports were stopped successfully. > Defaults to :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and at lease one port > - failed to close. > - """ > - port_close_output =3D self.send_command("port close all") > - if verify: > - num_ports =3D len(self.ports) > - if not all(f"Port {p_id} is closed" in port_close_output for > p_id in range(num_ports)): > - raise InteractiveCommandExecutionError("Ports were not > closed successfully.") > - > - def show_port_info_all(self) -> list[TestPmdPort]: > - """Returns the information of all the ports. > - > - Returns: > - list[TestPmdPort]: A list containing all the ports > information as `TestPmdPort`. > - """ > - output =3D self.send_command("show port info all") > - > - # Sample output of the "all" command looks like: > - # > - # > - # > - # ********************* Infos for port 0 ********************* > - # Key: value > - # > - # ********************* Infos for port 1 ********************* > - # Key: value > - # > - # > - # Takes advantage of the double new line in between ports as end > delimiter. But we need to > - # artificially add a new line at the end to pick up the last > port. Because commands are > - # executed on a pseudo-terminal created by paramiko on the remot= e > node, lines end with CRLF. > - # Therefore we also need to take the carriage return into accoun= t. > - iter =3D re.finditer(r"\*{21}.*?[\r\n]{4}", output + "\r\n", re.= S) > - self._ports =3D [TestPmdPort.parse(block.group(0)) for block in > iter] > - return self._ports > - > - def show_port_info(self, port_id: int) -> TestPmdPort: > - """Returns the given port information. > - > - Args: > - port_id: The port ID to gather information for. > - > - Raises: > - InteractiveCommandExecutionError: If `port_id` is invalid. > - > - Returns: > - TestPmdPort: An instance of `TestPmdPort` containing the > given port's information. > - """ > - output =3D self.send_command(f"show port info {port_id}", > skip_first_line=3DTrue) > - if output.startswith("Invalid port"): > - raise InteractiveCommandExecutionError("invalid port given") > - > - port =3D TestPmdPort.parse(output) > - self._update_port(port) > - return port > - > - def _update_port(self, port: TestPmdPort) -> None: > - if self._ports: > - self._ports =3D [ > - existing_port if port.id !=3D existing_port.id else port > - for existing_port in self._ports > - ] > - > - def set_mac_addr(self, port_id: int, mac_address: str, add: bool, > verify: bool =3D True) -> None: > - """Add or remove a mac address on a given port's Allowlist. > - > - Args: > - port_id: The port ID the mac address is set on. > - mac_address: The mac address to be added to or removed from > the specified port. > - add: If :data:`True`, add the specified mac address. If > :data:`False`, remove specified > - mac address. > - verify: If :data:'True', assert that the 'mac_addr' operatio= n > was successful. If > - :data:'False', run the command and skip this assertion. > - > - Raises: > - InteractiveCommandExecutionError: If the set mac address > operation fails. > - """ > - mac_cmd =3D "add" if add else "remove" > - output =3D self.send_command(f"mac_addr {mac_cmd} {port_id} > {mac_address}") > - if "Bad arguments" in output: > - self._logger.debug("Invalid argument provided to mac_addr") > - raise InteractiveCommandExecutionError("Invalid argument > provided") > - > - if verify: > - if "mac_addr_cmd error:" in output: > - self._logger.debug(f"Failed to {mac_cmd} {mac_address} o= n > port {port_id}") > - raise InteractiveCommandExecutionError( > - f"Failed to {mac_cmd} {mac_address} on port {port_id= } > \n{output}" > - ) > - > - def set_multicast_mac_addr( > - self, port_id: int, multi_addr: str, add: bool, verify: bool =3D > True > - ) -> None: > - """Add or remove multicast mac address to a specified port's > allow list. > - > - Args: > - port_id: The port ID the multicast address is set on. > - multi_addr: The multicast address to be added or removed fro= m > the filter. > - add: If :data:'True', add the specified multicast address to > the port filter. > - If :data:'False', remove the specified multicast address > from the port filter. > - verify: If :data:'True', assert that the 'mcast_addr' > operations was successful. > - If :data:'False', execute the 'mcast_addr' operation and > skip the assertion. > - > - Raises: > - InteractiveCommandExecutionError: If either the 'add' or > 'remove' operations fails. > - """ > - mcast_cmd =3D "add" if add else "remove" > - output =3D self.send_command(f"mcast_addr {mcast_cmd} {port_id} > {multi_addr}") > - if "Bad arguments" in output: > - self._logger.debug("Invalid arguments provided to mcast_addr= ") > - raise InteractiveCommandExecutionError("Invalid argument > provided") > - > - if verify: > - if ( > - "Invalid multicast_addr" in output > - or f"multicast address {'already' if add else 'not'} > filtered by port" in output > - ): > - self._logger.debug(f"Failed to {mcast_cmd} {multi_addr} > on port {port_id}") > - raise InteractiveCommandExecutionError( > - f"Failed to {mcast_cmd} {multi_addr} on port > {port_id} \n{output}" > - ) > - > - def show_port_stats_all(self) -> Tuple[list[TestPmdPortStats], str]: > - """Returns the statistics of all the ports. > - > - Returns: > - Tuple[str, list[TestPmdPortStats]]: A tuple where the first > element is the stats of all > - ports as `TestPmdPortStats` and second is the raw testpmd > output that was collected > - from the sent command. > - """ > - output =3D self.send_command("show port stats all") > - > - # Sample output of the "all" command looks like: > - # > - # ########### NIC statistics for port 0 ########### > - # values... > - # ################################################# > - # > - # ########### NIC statistics for port 1 ########### > - # values... > - # ################################################# > - # > - iter =3D re.finditer(r"(^ #*.+#*$[^#]+)^ #*\r$", output, > re.MULTILINE) > - return ([TestPmdPortStats.parse(block.group(1)) for block in > iter], output) > - > - def show_port_stats(self, port_id: int) -> TestPmdPortStats: > - """Returns the given port statistics. > - > - Args: > - port_id: The port ID to gather information for. > - > - Raises: > - InteractiveCommandExecutionError: If `port_id` is invalid. > - > - Returns: > - TestPmdPortStats: An instance of `TestPmdPortStats` > containing the given port's stats. > - """ > - output =3D self.send_command(f"show port stats {port_id}", > skip_first_line=3DTrue) > - if output.startswith("Invalid port"): > - raise InteractiveCommandExecutionError("invalid port given") > - > - return TestPmdPortStats.parse(output) > - > - def set_multicast_all(self, on: bool, verify: bool =3D True) -> None= : > - """Turns multicast mode on/off for the specified port. > - > - Args: > - on: If :data:`True`, turns multicast mode on, otherwise turn= s > off. > - verify: If :data:`True` an additional command will be sent t= o > verify > - that multicast mode is properly set. Defaults to > :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and multicast > - mode is not properly set. > - """ > - multicast_cmd_output =3D self.send_command(f"set allmulti all {'= on' > if on else 'off'}") > - if verify: > - port_stats =3D self.show_port_info_all() > - if on ^ all(stats.is_allmulticast_mode_enabled for stats in > port_stats): > - self._logger.debug( > - f"Failed to set multicast mode on all ports.: > \n{multicast_cmd_output}" > - ) > - raise InteractiveCommandExecutionError( > - "Testpmd failed to set multicast mode on all ports." > - ) > - > - @requires_stopped_ports > - def csum_set_hw( > - self, layers: ChecksumOffloadOptions, port_id: int, verify: bool > =3D True > - ) -> None: > - """Enables hardware checksum offloading on the specified layer. > - > - Args: > - layers: The layer/layers that checksum offloading should be > enabled on. > - port_id: The port number to enable checksum offloading on, > should be within 0-32. > - verify: If :data:`True` the output of the command will be > scanned in an attempt to > - verify that checksum offloading was enabled on the port. > - > - Raises: > - InteractiveCommandExecutionError: If checksum offload is not > enabled successfully. > - """ > - for name, offload in ChecksumOffloadOptions.__members__.items(): > - if offload in layers: > - name =3D name.replace("_", "-") > - csum_output =3D self.send_command(f"csum set {name} hw > {port_id}") > - if verify: > - if ( > - "Bad arguments" in csum_output > - or f"Please stop port {port_id} first" in > csum_output > - or f"checksum offload is not supported by port > {port_id}" in csum_output > - ): > - self._logger.debug(f"Csum set hw > error:\n{csum_output}") > - raise InteractiveCommandExecutionError( > - f"Failed to set csum hw {name} mode on port > {port_id}" > - ) > - success =3D False > - if f"{name} checksum offload is hw" in > csum_output.lower(): > - success =3D True > - if not success and verify: > - self._logger.debug( > - f"Failed to set csum hw mode on port > {port_id}:\n{csum_output}" > - ) > - raise InteractiveCommandExecutionError( > - f"""Failed to set csum hw mode on port > - > {port_id}:\n{csum_output}""" > - ) > - > - def flow_create(self, flow_rule: FlowRule, port_id: int) -> int: > - """Creates a flow rule in the testpmd session. > - > - This command is implicitly verified as needed to return the > created flow rule id. > - > - Args: > - flow_rule: :class:`FlowRule` object used for creating testpm= d > flow rule. > - port_id: Integer representing the port to use. > - > - Raises: > - InteractiveCommandExecutionError: If flow rule is invalid. > - > - Returns: > - Id of created flow rule. > - """ > - flow_output =3D self.send_command(f"flow create {port_id} > {flow_rule}") > - match =3D re.search(r"#(\d+)", flow_output) > - if match is not None: > - match_str =3D match.group(1) > - flow_id =3D int(match_str) > - return flow_id > - else: > - self._logger.debug(f"Failed to create flow > rule:\n{flow_output}") > - raise InteractiveCommandExecutionError(f"Failed to create > flow rule:\n{flow_output}") > - > - def flow_validate(self, flow_rule: FlowRule, port_id: int) -> bool: > - """Validates a flow rule in the testpmd session. > - > - Args: > - flow_rule: :class:`FlowRule` object used for validating > testpmd flow rule. > - port_id: Integer representing the port to use. > - > - Returns: > - Boolean representing whether rule is valid or not. > - """ > - flow_output =3D self.send_command(f"flow validate {port_id} > {flow_rule}") > - if "Flow rule validated" in flow_output: > - return True > - return False > - > - def flow_delete(self, flow_id: int, port_id: int, verify: bool =3D > True) -> None: > - """Deletes the specified flow rule from the testpmd session. > - > - Args: > - flow_id: ID of the flow to remove. > - port_id: Integer representing the port to use. > - verify: If :data:`True`, the output of the command is scanne= d > - to ensure the flow rule was deleted successfully. > - > - Raises: > - InteractiveCommandExecutionError: If flow rule is invalid. > - """ > - flow_output =3D self.send_command(f"flow destroy {port_id} rule > {flow_id}") > - if verify: > - if "destroyed" not in flow_output: > - self._logger.debug(f"Failed to delete flow > rule:\n{flow_output}") > - raise InteractiveCommandExecutionError( > - f"Failed to delete flow rule:\n{flow_output}" > - ) > - > - @requires_started_ports > - @requires_stopped_ports > - def set_port_mtu(self, port_id: int, mtu: int, verify: bool =3D True= ) > -> None: > - """Change the MTU of a port using testpmd. > - > - Some PMDs require that the port be stopped before changing the > MTU, and it does no harm to > - stop the port before configuring in cases where it isn't > required, so ports are stopped > - prior to changing their MTU. On the other hand, some PMDs requir= e > that the port had already > - been started once since testpmd startup. Therefore, ports are > also started before stopping > - them to ensure this has happened. > - > - Args: > - port_id: ID of the port to adjust the MTU on. > - mtu: Desired value for the MTU to be set to. > - verify: If `verify` is :data:`True` then the output will be > scanned in an attempt to > - verify that the mtu was properly set on the port. > Defaults to :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the MTU was not > - properly updated on the port matching `port_id`. > - """ > - set_mtu_output =3D self.send_command(f"port config mtu {port_id} > {mtu}") > - if verify and (f"MTU: {mtu}" not in self.send_command(f"show por= t > info {port_id}")): > - self._logger.debug( > - f"Failed to set mtu to {mtu} on port {port_id}. Output > was:\n{set_mtu_output}" > - ) > - raise InteractiveCommandExecutionError( > - f"Test pmd failed to update mtu of port {port_id} to > {mtu}" > - ) > - > - def set_port_mtu_all(self, mtu: int, verify: bool =3D True) -> None: > - """Change the MTU of all ports using testpmd. > - > - Runs :meth:`set_port_mtu` for every port that testpmd is aware o= f. > - > - Args: > - mtu: Desired value for the MTU to be set to. > - verify: Whether to verify that setting the MTU on each port > was successful or not. > - Defaults to :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the MTU was not > - properly updated on at least one port. > - """ > - for port in self.ports: > - self.set_port_mtu(port.id, mtu, verify) > - > - @staticmethod > - def extract_verbose_output(output: str) -> list[TestPmdVerbosePacket= ]: > - """Extract the verbose information present in given testpmd > output. > - > - This method extracts sections of verbose output that begin with > the line > - "port X/queue Y: sent/received Z packets" and end with the > ol_flags of a packet. > - > - Args: > - output: Testpmd output that contains verbose information > - > - Returns: > - List of parsed packet information gathered from verbose > information in `output`. > - """ > - out: list[TestPmdVerbosePacket] =3D [] > - prev_header: str =3D "" > - iter =3D re.finditer( > - r"(?P
(?:port \d+/queue \d+: (?:received|sent) \d+ > packets)?)\s*" > - r"(?Psrc=3D[\w\s=3D:-]+?ol_flags: [\w ]+)", > - output, > - ) > - for match in iter: > - if match.group("HEADER"): > - prev_header =3D match.group("HEADER") > - > out.append(TestPmdVerbosePacket.parse(f"{prev_header}\n{match.group('PACK= ET')}")) > - return out > - > - @requires_stopped_ports > - def set_vlan_filter(self, port: int, enable: bool, verify: bool =3D > True) -> None: > - """Set vlan filter on. > - > - Args: > - port: The port number to enable VLAN filter on. > - enable: Enable the filter on `port` if :data:`True`, > otherwise disable it. > - verify: If :data:`True`, the output of the command and show > port info > - is scanned to verify that vlan filtering was set > successfully. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the filter > - fails to update. > - """ > - filter_cmd_output =3D self.send_command(f"vlan set filter {'on' = if > enable else 'off'} {port}") > - if verify: > - vlan_settings =3D self.show_port_info(port_id=3Dport).vlan_o= ffload > - if enable ^ (vlan_settings is not None and > VLANOffloadFlag.FILTER in vlan_settings): > - self._logger.debug( > - f"""Failed to {"enable" if enable else "disable"} > - filter on port {port}: > \n{filter_cmd_output}""" > - ) > - raise InteractiveCommandExecutionError( > - f"""Failed to {"enable" if enable else "disable"} > - filter on port {port}""" > - ) > - > - def set_mac_address(self, port: int, mac_address: str, verify: bool = =3D > True) -> None: > - """Set port's MAC address. > - > - Args: > - port: The number of the requested port. > - mac_address: The MAC address to set. > - verify: If :data:`True`, the output of the command is scanne= d > to verify that > - the mac address is set in the specified port. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the command > - fails to execute. > - """ > - output =3D self.send_command(f"mac_addr set {port} {mac_address}= ", > skip_first_line=3DTrue) > - if verify: > - if output.strip(): > - self._logger.debug( > - f"Testpmd failed to set MAC address {mac_address} on > port {port}." > - ) > - raise InteractiveCommandExecutionError( > - f"Testpmd failed to set MAC address {mac_address} on > port {port}." > - ) > - > - def set_flow_control( > - self, port: int, flow_ctrl: TestPmdPortFlowCtrl, verify: bool = =3D > True > - ) -> None: > - """Set the given `port`'s flow control. > - > - Args: > - port: The number of the requested port. > - flow_ctrl: The requested flow control parameters. > - verify: If :data:`True`, the output of the command is scanne= d > to verify that > - the flow control in the specified port is set. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the command > - fails to execute. > - """ > - output =3D self.send_command(f"set flow_ctrl {flow_ctrl} {port}"= , > skip_first_line=3DTrue) > - if verify: > - if output.strip(): > - self._logger.debug(f"Testpmd failed to set the > {flow_ctrl} in port {port}.") > - raise InteractiveCommandExecutionError( > - f"Testpmd failed to set the {flow_ctrl} in port > {port}." > - ) > - > - def show_port_flow_info(self, port: int) -> TestPmdPortFlowCtrl | > None: > - """Show port info flow. > - > - Args: > - port: The number of the requested port. > - > - Returns: > - The current port flow control parameters if supported, > otherwise :data:`None`. > - """ > - output =3D self.send_command(f"show port {port} flow_ctrl") > - if "Flow control infos" in output: > - return TestPmdPortFlowCtrl.parse(output) > - return None > - > - @requires_stopped_ports > - def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool =3D > True) -> None: > - """Add specified vlan tag to the filter list on a port. Requires > vlan filter to be on. > - > - Args: > - vlan: The vlan tag to add, should be within 1-1005. > - port: The port number to add the tag on. > - add: Adds the tag if :data:`True`, otherwise removes the tag= . > - verify: If :data:`True`, the output of the command is scanne= d > to verify that > - the vlan tag was added to the filter list on the > specified port. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the tag > - is not added. > - """ > - rx_cmd_output =3D self.send_command(f"rx_vlan {'add' if add else > 'rm'} {vlan} {port}") > - if verify: > - if ( > - "VLAN-filtering disabled" in rx_cmd_output > - or "Invalid vlan_id" in rx_cmd_output > - or "Bad arguments" in rx_cmd_output > - ): > - self._logger.debug( > - f"""Failed to {"add" if add else "remove"} tag {vlan= } > - port {port}: \n{rx_cmd_output}""" > - ) > - raise InteractiveCommandExecutionError( > - f"Testpmd failed to {'add' if add else 'remove'} tag > {vlan} on port {port}." > - ) > - > - @requires_stopped_ports > - def set_vlan_strip(self, port: int, enable: bool, verify: bool =3D > True) -> None: > - """Enable or disable vlan stripping on the specified port. > - > - Args: > - port: The port number to use. > - enable: If :data:`True`, will turn vlan stripping on, > otherwise will turn off. > - verify: If :data:`True`, the output of the command and show > port info > - is scanned to verify that vlan stripping was enabled on > the specified port. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and stripping > - fails to update. > - """ > - strip_cmd_output =3D self.send_command(f"vlan set strip {'on' if > enable else 'off'} {port}") > - if verify: > - vlan_settings =3D self.show_port_info(port_id=3Dport).vlan_o= ffload > - if enable ^ (vlan_settings is not None and > VLANOffloadFlag.STRIP in vlan_settings): > - self._logger.debug( > - f"""Failed to set strip {"on" if enable else "off"} > - port {port}: \n{strip_cmd_output}""" > - ) > - raise InteractiveCommandExecutionError( > - f"Testpmd failed to set strip {'on' if enable else > 'off'} port {port}." > - ) > - > - @requires_stopped_ports > - def tx_vlan_set( > - self, port: int, enable: bool, vlan: int | None =3D None, verify= : > bool =3D True > - ) -> None: > - """Set hardware insertion of vlan tags in packets sent on a port= . > - > - Args: > - port: The port number to use. > - enable: Sets vlan tag insertion if :data:`True`, and resets > if :data:`False`. > - vlan: The vlan tag to insert if enable is :data:`True`. > - verify: If :data:`True`, the output of the command is scanne= d > to verify that > - vlan insertion was enabled on the specified port. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the insertion > - tag is not set. > - """ > - if enable: > - tx_vlan_cmd_output =3D self.send_command(f"tx_vlan set {port= } > {vlan}") > - if verify: > - if ( > - "Please stop port" in tx_vlan_cmd_output > - or "Invalid vlan_id" in tx_vlan_cmd_output > - or "Invalid port" in tx_vlan_cmd_output > - ): > - self._logger.debug( > - f"Failed to set vlan tag {vlan} on port > {port}:\n{tx_vlan_cmd_output}" > - ) > - raise InteractiveCommandExecutionError( > - f"Testpmd failed to set vlan insertion tag {vlan= } > on port {port}." > - ) > - else: > - tx_vlan_cmd_output =3D self.send_command(f"tx_vlan reset > {port}") > - if verify: > - if "Please stop port" in tx_vlan_cmd_output or "Invalid > port" in tx_vlan_cmd_output: > - self._logger.debug( > - f"Failed to reset vlan insertion on port {port}: > \n{tx_vlan_cmd_output}" > - ) > - raise InteractiveCommandExecutionError( > - f"Testpmd failed to reset vlan insertion on port > {port}." > - ) > - > - def set_promisc(self, port: int, enable: bool, verify: bool =3D True= ) > -> None: > - """Enable or disable promiscuous mode for the specified port. > - > - Args: > - port: Port number to use. > - enable: If :data:`True`, turn promiscuous mode on, otherwise > turn off. > - verify: If :data:`True` an additional command will be sent t= o > verify that > - promiscuous mode is properly set. Defaults to > :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and promiscuous mode > - is not correctly set. > - """ > - promisc_cmd_output =3D self.send_command(f"set promisc {port} {'= on' > if enable else 'off'}") > - if verify: > - stats =3D self.show_port_info(port_id=3Dport) > - if enable ^ stats.is_promiscuous_mode_enabled: > - self._logger.debug( > - f"Failed to set promiscuous mode on port {port}: > \n{promisc_cmd_output}" > - ) > - raise InteractiveCommandExecutionError( > - f"Testpmd failed to set promiscuous mode on port > {port}." > - ) > - > - def set_verbose(self, level: int, verify: bool =3D True) -> None: > - """Set debug verbosity level. > - > - Args: > - level: 0 - silent except for error > - 1 - fully verbose except for Tx packets > - 2 - fully verbose except for Rx packets > - >2 - fully verbose > - verify: If :data:`True` the command output will be scanned t= o > verify that verbose level > - is properly set. Defaults to :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and verbose level > - is not correctly set. > - """ > - verbose_cmd_output =3D self.send_command(f"set verbose {level}") > - if verify: > - if "Change verbose level" not in verbose_cmd_output: > - self._logger.debug( > - f"Failed to set verbose level to {level}: > \n{verbose_cmd_output}" > - ) > - raise InteractiveCommandExecutionError( > - f"Testpmd failed to set verbose level to {level}." > - ) > - > - def rx_vxlan(self, vxlan_id: int, port_id: int, enable: bool, verify= : > bool =3D True) -> None: > - """Add or remove vxlan id to/from filter list. > - > - Args: > - vxlan_id: VXLAN ID to add to port filter list. > - port_id: ID of the port to modify VXLAN filter of. > - enable: If :data:`True`, adds specified VXLAN ID, otherwise > removes it. > - verify: If :data:`True`, the output of the command is checke= d > to verify > - the VXLAN ID was successfully added/removed from the por= t. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and VXLAN ID > - is not successfully added or removed. > - """ > - action =3D "add" if enable else "rm" > - vxlan_output =3D self.send_command(f"rx_vxlan_port {action} > {vxlan_id} {port_id}") > - if verify: > - if "udp tunneling add error" in vxlan_output: > - self._logger.debug(f"Failed to set > VXLAN:\n{vxlan_output}") > - raise InteractiveCommandExecutionError(f"Failed to set > VXLAN:\n{vxlan_output}") > - > - def clear_port_stats(self, port_id: int, verify: bool =3D True) -> N= one: > - """Clear statistics of a given port. > - > - Args: > - port_id: ID of the port to clear the statistics on. > - verify: If :data:`True` the output of the command will be > scanned to verify that it was > - successful, otherwise failures will be ignored. Defaults > to :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and testpmd fails to > - clear the statistics of the given port. > - """ > - clear_output =3D self.send_command(f"clear port stats {port_id}"= ) > - if verify and f"NIC statistics for port {port_id} cleared" not i= n > clear_output: > - raise InteractiveCommandExecutionError( > - f"Test pmd failed to set clear forwarding stats on port > {port_id}" > - ) > - > - def clear_port_stats_all(self, verify: bool =3D True) -> None: > - """Clear the statistics of all ports that testpmd is aware of. > - > - Args: > - verify: If :data:`True` the output of the command will be > scanned to verify that all > - ports had their statistics cleared, otherwise failures > will be ignored. Defaults to > - :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and testpmd fails to > - clear the statistics of any of its ports. > - """ > - clear_output =3D self.send_command("clear port stats all") > - if verify: > - if type(self._app_params.port_numa_config) is list: > - for port_id in > range(len(self._app_params.port_numa_config)): > - if f"NIC statistics for port {port_id} cleared" not > in clear_output: > - raise InteractiveCommandExecutionError( > - f"Test pmd failed to set clear forwarding > stats on port {port_id}" > - ) > - > - @only_active > - def close(self) -> None: > - """Overrides :meth:`~.interactive_shell.close`.""" > - self.stop() > - self.send_command("quit", "Bye...") > - return super().close() > - > - """ > - =3D=3D=3D=3D=3D=3D Capability retrieval methods =3D=3D=3D=3D=3D=3D > - """ > - > - def get_capabilities_rx_offload( > - self, > - supported_capabilities: MutableSet["NicCapability"], > - unsupported_capabilities: MutableSet["NicCapability"], > - ) -> None: > - """Get all rx offload capabilities and divide them into supporte= d > and unsupported. > - > - Args: > - supported_capabilities: Supported capabilities will be added > to this set. > - unsupported_capabilities: Unsupported capabilities will be > added to this set. > - """ > - self._logger.debug("Getting rx offload capabilities.") > - command =3D f"show port {self.ports[0].id} rx_offload capabiliti= es" > - rx_offload_capabilities_out =3D self.send_command(command) > - rx_offload_capabilities =3D > RxOffloadCapabilities.parse(rx_offload_capabilities_out) > - self._update_capabilities_from_flag( > - supported_capabilities, > - unsupported_capabilities, > - RxOffloadCapability, > - rx_offload_capabilities.per_port | > rx_offload_capabilities.per_queue, > - ) > - > - def get_port_queue_info( > - self, port_id: int, queue_id: int, is_rx_queue: bool > - ) -> TestPmdQueueInfo: > - """Returns the current state of the specified queue.""" > - command =3D f"show {'rxq' if is_rx_queue else 'txq'} info {port_= id} > {queue_id}" > - queue_info =3D TestPmdQueueInfo.parse(self.send_command(command)= ) > - return queue_info > - > - def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: > bool) -> None: > - """Setup a given queue on a port. > - > - This functionality cannot be verified because the setup action > only takes effect when the > - queue is started. > - > - Args: > - port_id: ID of the port where the queue resides. > - queue_id: ID of the queue to setup. > - is_rx_queue: Type of queue to setup. If :data:`True` an RX > queue will be setup, > - otherwise a TX queue will be setup. > - """ > - self.send_command(f"port {port_id} {'rxq' if is_rx_queue else > 'txq'} {queue_id} setup") > - > - def stop_port_queue( > - self, port_id: int, queue_id: int, is_rx_queue: bool, verify: > bool =3D True > - ) -> None: > - """Stops a given queue on a port. > - > - Args: > - port_id: ID of the port that the queue belongs to. > - queue_id: ID of the queue to stop. > - is_rx_queue: Type of queue to stop. If :data:`True` an RX > queue will be stopped, > - otherwise a TX queue will be stopped. > - verify: If :data:`True` an additional command will be sent t= o > verify the queue stopped. > - Defaults to :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the queue fails to > - stop. > - """ > - port_type =3D "rxq" if is_rx_queue else "txq" > - stop_cmd_output =3D self.send_command(f"port {port_id} {port_typ= e} > {queue_id} stop") > - if verify: > - queue_started =3D self.get_port_queue_info( > - port_id, queue_id, is_rx_queue > - ).is_queue_started > - if queue_started: > - self._logger.debug( > - f"Failed to stop {port_type} {queue_id} on port > {port_id}:\n{stop_cmd_output}" > - ) > - raise InteractiveCommandExecutionError( > - f"Test pmd failed to stop {port_type} {queue_id} on > port {port_id}" > - ) > - > - def start_port_queue( > - self, port_id: int, queue_id: int, is_rx_queue: bool, verify: > bool =3D True > - ) -> None: > - """Starts a given queue on a port. > - > - First sets up the port queue, then starts it. > - > - Args: > - port_id: ID of the port that the queue belongs to. > - queue_id: ID of the queue to start. > - is_rx_queue: Type of queue to start. If :data:`True` an RX > queue will be started, > - otherwise a TX queue will be started. > - verify: if :data:`True` an additional command will be sent t= o > verify that the queue was > - started. Defaults to :data:`True`. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and the queue fails to > - start. > - """ > - port_type =3D "rxq" if is_rx_queue else "txq" > - self.setup_port_queue(port_id, queue_id, is_rx_queue) > - start_cmd_output =3D self.send_command(f"port {port_id} {port_ty= pe} > {queue_id} start") > - if verify: > - queue_started =3D self.get_port_queue_info( > - port_id, queue_id, is_rx_queue > - ).is_queue_started > - if not queue_started: > - self._logger.debug( > - f"Failed to start {port_type} {queue_id} on port > {port_id}:\n{start_cmd_output}" > - ) > - raise InteractiveCommandExecutionError( > - f"Test pmd failed to start {port_type} {queue_id} on > port {port_id}" > - ) > - > - def get_queue_ring_size(self, port_id: int, queue_id: int, > is_rx_queue: bool) -> int: > - """Returns the current size of the ring on the specified queue."= "" > - command =3D f"show {'rxq' if is_rx_queue else 'txq'} info {port_= id} > {queue_id}" > - queue_info =3D TestPmdQueueInfo.parse(self.send_command(command)= ) > - return queue_info.ring_size > - > - def set_queue_ring_size( > - self, > - port_id: int, > - queue_id: int, > - size: int, > - is_rx_queue: bool, > - verify: bool =3D True, > - ) -> None: > - """Update the ring size of an Rx/Tx queue on a given port. > - > - Queue is setup after setting the ring size so that the queue inf= o > reflects this change and > - it can be verified. > - > - Args: > - port_id: The port that the queue resides on. > - queue_id: The ID of the queue on the port. > - size: The size to update the ring size to. > - is_rx_queue: Whether to modify an RX or TX queue. If > :data:`True` an RX queue will be > - updated, otherwise a TX queue will be updated. > - verify: If :data:`True` an additional command will be sent t= o > check the ring size of > - the queue in an attempt to validate that the size was > changes properly. > - > - Raises: > - InteractiveCommandExecutionError: If `verify` is :data:`True= ` > and there is a failure > - when updating ring size. > - """ > - queue_type =3D "rxq" if is_rx_queue else "txq" > - self.send_command(f"port config {port_id} {queue_type} {queue_id= } > ring_size {size}") > - self.setup_port_queue(port_id, queue_id, is_rx_queue) > - if verify: > - curr_ring_size =3D self.get_queue_ring_size(port_id, queue_i= d, > is_rx_queue) > - if curr_ring_size !=3D size: > - self._logger.debug( > - f"Failed up update ring size of queue {queue_id} on > port {port_id}. Current" > - f" ring size is {curr_ring_size}." > - ) > - raise InteractiveCommandExecutionError( > - f"Failed to update ring size of queue {queue_id} on > port {port_id}" > - ) > - > - @requires_stopped_ports > - def set_queue_deferred_start( > - self, port_id: int, queue_id: int, is_rx_queue: bool, on: bool > - ) -> None: > - """Set the deferred start attribute of the specified queue on/of= f. > - > - Args: > - port_id: The port that the queue resides on. > - queue_id: The ID of the queue on the port. > - is_rx_queue: Whether to modify an RX or TX queue. If > :data:`True` an RX queue will be > - updated, otherwise a TX queue will be updated. > - on: Whether to set deferred start mode on or off. If > :data:`True` deferred start will > - be turned on, otherwise it will be turned off. > - """ > - queue_type =3D "rxq" if is_rx_queue else "txq" > - action =3D "on" if on else "off" > - self.send_command(f"port {port_id} {queue_type} {queue_id} > deferred_start {action}") > - > - def _update_capabilities_from_flag( > - self, > - supported_capabilities: MutableSet["NicCapability"], > - unsupported_capabilities: MutableSet["NicCapability"], > - flag_class: type[Flag], > - supported_flags: Flag, > - ) -> None: > - """Divide all flags from `flag_class` into supported and > unsupported.""" > - for flag in flag_class: > - if flag in supported_flags: > - supported_capabilities.add(NicCapability[str(flag.name)]= ) > - else: > - unsupported_capabilities.add(NicCapability[str(flag.name > )]) > - > - @requires_started_ports > - def get_capabilities_rxq_info( > - self, > - supported_capabilities: MutableSet["NicCapability"], > - unsupported_capabilities: MutableSet["NicCapability"], > - ) -> None: > - """Get all rxq capabilities and divide them into supported and > unsupported. > - > - Args: > - supported_capabilities: Supported capabilities will be added > to this set. > - unsupported_capabilities: Unsupported capabilities will be > added to this set. > - """ > - self._logger.debug("Getting rxq capabilities.") > - command =3D f"show rxq info {self.ports[0].id} 0" > - rxq_info =3D TestPmdRxqInfo.parse(self.send_command(command)) > - if rxq_info.scattered_packets: > - supported_capabilities.add(NicCapability.SCATTERED_RX_ENABLE= D) > - else: > - > unsupported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED) > - > - def get_capabilities_show_port_info( > - self, > - supported_capabilities: MutableSet["NicCapability"], > - unsupported_capabilities: MutableSet["NicCapability"], > - ) -> None: > - """Get all capabilities from show port info and divide them into > supported and unsupported. > - > - Args: > - supported_capabilities: Supported capabilities will be added > to this set. > - unsupported_capabilities: Unsupported capabilities will be > added to this set. > - """ > - self._update_capabilities_from_flag( > - supported_capabilities, > - unsupported_capabilities, > - DeviceCapabilitiesFlag, > - self.ports[0].device_capabilities, > - ) > - > - def get_capabilities_mcast_filtering( > - self, > - supported_capabilities: MutableSet["NicCapability"], > - unsupported_capabilities: MutableSet["NicCapability"], > - ) -> None: > - """Get multicast filtering capability from mcast_addr add and > check for testpmd error code. > - > - Args: > - supported_capabilities: Supported capabilities will be added > to this set. > - unsupported_capabilities: Unsupported capabilities will be > added to this set. > - """ > - self._logger.debug("Getting mcast filter capabilities.") > - command =3D f"mcast_addr add {self.ports[0].id} 01:00:5E:00:00:0= 0" > - output =3D self.send_command(command) > - if "diag=3D-95" in output: > - unsupported_capabilities.add(NicCapability.MCAST_FILTERING) > - else: > - supported_capabilities.add(NicCapability.MCAST_FILTERING) > - command =3D str.replace(command, "add", "remove", 1) > - self.send_command(command) > - > - def get_capabilities_flow_ctrl( > - self, > - supported_capabilities: MutableSet["NicCapability"], > - unsupported_capabilities: MutableSet["NicCapability"], > - ) -> None: > - """Get flow control capability and check for testpmd failure. > - > - Args: > - supported_capabilities: Supported capabilities will be added > to this set. > - unsupported_capabilities: Unsupported capabilities will be > added to this set. > - """ > - self._logger.debug("Getting flow ctrl capabilities.") > - command =3D f"show port {self.ports[0].id} flow_ctrl" > - output =3D self.send_command(command) > - if "Flow control infos" in output: > - supported_capabilities.add(NicCapability.FLOW_CTRL) > - else: > - unsupported_capabilities.add(NicCapability.FLOW_CTRL) > - > - def get_capabilities_physical_function( > - self, > - supported_capabilities: MutableSet["NicCapability"], > - unsupported_capabilities: MutableSet["NicCapability"], > - ) -> None: > - """Store capability representing a physical function test run. > - > - Args: > - supported_capabilities: Supported capabilities will be added > to this set. > - unsupported_capabilities: Unsupported capabilities will be > added to this set. > - """ > - ctx =3D get_ctx() > - if ctx.topology.vf_ports =3D=3D []: > - supported_capabilities.add(NicCapability.PHYSICAL_FUNCTION) > - else: > - unsupported_capabilities.add(NicCapability.PHYSICAL_FUNCTION= ) > - > - > -class NicCapability(NoAliasEnum): > - """A mapping between capability names and the associated > :class:`TestPmdShell` methods. > - > - The :class:`TestPmdShell` capability checking method executes the > command that checks > - whether the capability is supported. > - A decorator may optionally be added to the method that will add and > remove configuration > - that's necessary to retrieve the capability support status. > - The Enum members may be assigned the method itself or a tuple of > - (capability_checking_method, decorator_function). > - > - The signature of each :class:`TestPmdShell` capability checking > method must be:: > - > - fn(self, supported_capabilities: MutableSet, > unsupported_capabilities: MutableSet) -> None > - > - The capability checking method must get whether a capability is > supported or not > - from a testpmd command. If multiple capabilities can be obtained fro= m > a testpmd command, > - each should be obtained in the method. These capabilities should the= n > - be added to `supported_capabilities` or `unsupported_capabilities` > based on their support. > - > - The two dictionaries are shared across all capability discovery > function calls in a given > - test run so that we don't call the same function multiple times. For > example, when we find > - :attr:`SCATTERED_RX_ENABLED` in > :meth:`TestPmdShell.get_capabilities_rxq_info`, > - we don't go looking for it again if a different test case also needs > it. > - """ > - > - #: Scattered packets Rx enabled > - SCATTERED_RX_ENABLED: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rxq_info, > - add_remove_mtu(9000), > - ) > - #: > - RX_OFFLOAD_VLAN_STRIP: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports L3 checksum offload. > - RX_OFFLOAD_IPV4_CKSUM: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports L4 checksum offload. > - RX_OFFLOAD_UDP_CKSUM: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports L4 checksum offload. > - RX_OFFLOAD_TCP_CKSUM: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports Large Receive Offload. > - RX_OFFLOAD_TCP_LRO: TestPmdShellNicCapability =3D > (TestPmdShell.get_capabilities_rx_offload, None) > - #: Device supports QinQ (queue in queue) offload. > - RX_OFFLOAD_QINQ_STRIP: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports inner packet L3 checksum. > - RX_OFFLOAD_OUTER_IPV4_CKSUM: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports MACsec. > - RX_OFFLOAD_MACSEC_STRIP: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports filtering of a VLAN Tag identifier. > - RX_OFFLOAD_VLAN_FILTER: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports VLAN offload. > - RX_OFFLOAD_VLAN_EXTEND: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports receiving segmented mbufs. > - RX_OFFLOAD_SCATTER: TestPmdShellNicCapability =3D > (TestPmdShell.get_capabilities_rx_offload, None) > - #: Device supports Timestamp. > - RX_OFFLOAD_TIMESTAMP: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports crypto processing while packet is received in NIC= . > - RX_OFFLOAD_SECURITY: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports CRC stripping. > - RX_OFFLOAD_KEEP_CRC: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports L4 checksum offload. > - RX_OFFLOAD_SCTP_CKSUM: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports inner packet L4 checksum. > - RX_OFFLOAD_OUTER_UDP_CKSUM: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports RSS hashing. > - RX_OFFLOAD_RSS_HASH: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports scatter Rx packets to segmented mbufs. > - RX_OFFLOAD_BUFFER_SPLIT: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports all checksum capabilities. > - RX_OFFLOAD_CHECKSUM: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_rx_offload, > - None, > - ) > - #: Device supports all VLAN capabilities. > - RX_OFFLOAD_VLAN: TestPmdShellNicCapability =3D > (TestPmdShell.get_capabilities_rx_offload, None) > - #: Device supports Rx queue setup after device started. > - RUNTIME_RX_QUEUE_SETUP: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_show_port_info, > - None, > - ) > - #: Device supports Tx queue setup after device started. > - RUNTIME_TX_QUEUE_SETUP: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_show_port_info, > - None, > - ) > - #: Device supports shared Rx queue among ports within Rx domain and > switch domain. > - RXQ_SHARE: TestPmdShellNicCapability =3D > (TestPmdShell.get_capabilities_show_port_info, None) > - #: Device supports keeping flow rules across restart. > - FLOW_RULE_KEEP: TestPmdShellNicCapability =3D > (TestPmdShell.get_capabilities_show_port_info, None) > - #: Device supports keeping shared flow objects across restart. > - FLOW_SHARED_OBJECT_KEEP: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_show_port_info, > - None, > - ) > - #: Device supports multicast address filtering. > - MCAST_FILTERING: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_mcast_filtering, > - None, > - ) > - #: Device supports flow ctrl. > - FLOW_CTRL: TestPmdShellNicCapability =3D > (TestPmdShell.get_capabilities_flow_ctrl, None) > - #: Device is running on a physical function. > - PHYSICAL_FUNCTION: TestPmdShellNicCapability =3D ( > - TestPmdShell.get_capabilities_physical_function, > - None, > - ) > - > - def __call__( > - self, > - testpmd_shell: TestPmdShell, > - supported_capabilities: MutableSet[Self], > - unsupported_capabilities: MutableSet[Self], > - ) -> None: > - """Execute the associated capability retrieval function. > - > - Args: > - testpmd_shell: :class:`TestPmdShell` object to which the > function will be bound to. > - supported_capabilities: The dictionary storing the supported > capabilities > - of a given test run. > - unsupported_capabilities: The dictionary storing the > unsupported capabilities > - of a given test run. > - """ > - self.value(testpmd_shell, supported_capabilities, > unsupported_capabilities) > diff --git a/dts/framework/testbed_model/capability.py > b/dts/framework/testbed_model/capability.py > index f895b22bb3..a4a8d9b7b4 100644 > --- a/dts/framework/testbed_model/capability.py > +++ b/dts/framework/testbed_model/capability.py > @@ -26,9 +26,9 @@ > .. code:: python > > from framework.test_suite import TestSuite, func_test > - from framework.testbed_model.capability import TopologyType, > requires > + from framework.testbed_model.capability import LinkTopology, > requires > # The whole test suite (each test case within) doesn't require > any links. > - @requires(topology_type=3DTopologyType.no_link) > + @requires_link_topology(LinkTopology.NO_LINK) > @func_test > class TestHelloWorld(TestSuite): > def hello_world_single_core(self): > @@ -41,7 +41,7 @@ def hello_world_single_core(self): > class TestPmdBufferScatter(TestSuite): > # only the test case requires the SCATTERED_RX_ENABLED > capability > # other test cases may not require it > - @requires(NicCapability.SCATTERED_RX_ENABLED) > + @requires_nic_capability(NicCapability.SCATTERED_RX_ENABLED) > @func_test > def test_scatter_mbuf_2048(self): > """ > @@ -50,27 +50,38 @@ def test_scatter_mbuf_2048(self): > from abc import ABC, abstractmethod > from collections.abc import MutableSet > from dataclasses import dataclass > -from typing import TYPE_CHECKING, Callable, ClassVar, Protocol > +from typing import ( > + TYPE_CHECKING, > + Any, > + Callable, > + ClassVar, > + Concatenate, > + ParamSpec, > + Protocol, > + TypeAlias, > +) > > from typing_extensions import Self > > +from api.capabilities import LinkTopology, NicCapability > from framework.exception import ConfigurationError, InternalError, > SkippedTestException > from framework.logger import get_dts_logger > -from framework.remote_session.testpmd_shell import ( > - NicCapability, > - TestPmdShell, > - TestPmdShellCapabilityMethod, > - TestPmdShellDecorator, > - TestPmdShellMethod, > -) > from framework.testbed_model.node import Node > from framework.testbed_model.port import DriverKind > - > -from .topology import Topology, TopologyType > +from framework.testbed_model.topology import Topology > > if TYPE_CHECKING: > + from api.testpmd import TestPmd > from framework.test_suite import TestCase > > +P =3D ParamSpec("P") > +TestPmdMethod =3D Callable[Concatenate["TestPmd", P], Any] > +TestPmdCapabilityMethod: TypeAlias =3D Callable[ > + ["TestPmd", MutableSet["NicCapability"], > MutableSet["NicCapability"]], None > +] > +TestPmdDecorator: TypeAlias =3D Callable[[TestPmdMethod], TestPmdMethod] > +TestPmdNicCapability =3D tuple[TestPmdCapabilityMethod, TestPmdDecorator= | > None] > + > > class Capability(ABC): > """The base class for various capabilities. > @@ -153,7 +164,7 @@ def __hash__(self) -> int: > > @dataclass > class DecoratedNicCapability(Capability): > - """A wrapper around > :class:`~framework.remote_session.testpmd_shell.NicCapability`. > + """A wrapper around :class:`~api.testpmd.NicCapability`. > > New instances should be created with the :meth:`create_unique` class > method to ensure > there are no duplicate instances. > @@ -166,10 +177,69 @@ class DecoratedNicCapability(Capability): > """ > > nic_capability: NicCapability > - capability_fn: TestPmdShellCapabilityMethod > - capability_decorator: TestPmdShellDecorator | None > + capability_fn: TestPmdCapabilityMethod > + capability_decorator: TestPmdDecorator | None > _unique_capabilities: ClassVar[dict[NicCapability, Self]] =3D {} > > + @classmethod > + def _get_nic_capability_check(cls) -> list[TestPmdNicCapability]: > + """A mapping between capability names and the associated > :class:`TestPmd` methods. > + > + The :class:`TestPmd` capability checking method executes the > command that checks > + whether the capability is supported. > + A decorator may optionally be added to the method that will add > and remove configuration > + that's necessary to retrieve the capability support status. > + The Enum members may be assigned the method itself or a tuple of > + (capability_checking_method, decorator_function). > + > + The signature of each :class:`TestPmd` capability checking metho= d > must be:: > + > + fn(self, supported_capabilities: MutableSet, > unsupported_capabilities: MutableSet) > + > + The capability checking method must get whether a capability is > supported or not > + from a testpmd command. If multiple capabilities can be obtained > from a testpmd command, > + each should be obtained in the method. These capabilities should > then > + be added to `supported_capabilities` or > `unsupported_capabilities` based on their support. > + > + The two dictionaries are shared across all capability discovery > function calls in a given > + test run so that we don't call the same function multiple times. > For example, when we find > + :attr:`SCATTERED_RX_ENABLED` in > :meth:`TestPmd.get_capabilities_rxq_info`, > + we don't go looking for it again if a different test case also > needs it. > + """ > + from api.testpmd import TestPmd, _add_remove_mtu > + > + return [ > + (TestPmd.get_capabilities_rxq_info, _add_remove_mtu(9000)), > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_VLAN_STRIP > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_IPV4_CKSUM > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_UDP_CKSUM > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_TCP_CKSUM > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_TCP_LRO > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_QINQ_STRIP > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_OUTER_IPV4_CKSUM > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_MACSEC_STRIP > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_VLAN_FILTER > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_VLAN_EXTEND > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_SCATTER > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_TIMESTAMP > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_SECURITY > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_KEEP_CRC > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_SCTP_CKSUM > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_OUTER_UDP_CKSUM > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_RSS_HASH > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_BUFFER_SPLIT > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_CHECKSUM > + (TestPmd.get_capabilities_rx_offload, None), # > RX_OFFLOAD_VLAN > + (TestPmd.get_capabilities_show_port_info, None), # > RUNTIME_RX_QUEUE_SETUP > + (TestPmd.get_capabilities_show_port_info, None), # > RUNTIME_TX_QUEUE_SETUP > + (TestPmd.get_capabilities_show_port_info, None), # RXQ_SHAR= E > + (TestPmd.get_capabilities_show_port_info, None), # > FLOW_RULE_KEEP > + (TestPmd.get_capabilities_show_port_info, None), # > FLOW_SHARED_OBJECT_KEEP > + (TestPmd.get_capabilities_mcast_filtering, None), # > MCAST_FILTERING > + (TestPmd.get_capabilities_flow_ctrl, None), # FLOW_CTRL > + (TestPmd.get_capabilities_physical_function, None), # > PHYSICAL_FUNCTION > + ] > + > @classmethod > def get_unique(cls, nic_capability: NicCapability) -> Self: > """Get the capability uniquely identified by `nic_capability`. > @@ -188,7 +258,7 @@ def get_unique(cls, nic_capability: NicCapability) -> > Self: > Returns: > The capability uniquely identified by `nic_capability`. > """ > - capability_fn, decorator_fn =3D nic_capability.value > + capability_fn, decorator_fn =3D > cls._get_nic_capability_check()[nic_capability.value] > > if nic_capability not in cls._unique_capabilities: > cls._unique_capabilities[nic_capability] =3D cls( > @@ -207,9 +277,11 @@ def get_supported_capabilities( > Each capability is first checked whether it's > supported/unsupported > before executing its `capability_fn` so that each capability is > retrieved only once. > """ > + from api.testpmd import TestPmd > + > supported_conditional_capabilities: set["DecoratedNicCapability"= ] > =3D set() > logger =3D get_dts_logger(f"{node.name}.{cls.__name__}") > - if topology.type is topology.type.no_link: > + if topology.type is topology.type.NO_LINK: > logger.debug( > "No links available in the current topology, not getting > NIC capabilities." > ) > @@ -219,7 +291,7 @@ def get_supported_capabilities( > ) > if cls.capabilities_to_check: > capabilities_to_check_map =3D > cls._get_decorated_capabilities_map() > - with TestPmdShell() as testpmd_shell: > + with TestPmd() as testpmd: > for ( > conditional_capability_fn, > capabilities, > @@ -231,7 +303,7 @@ def get_supported_capabilities( > ) > if conditional_capability_fn: > capability_fn =3D > conditional_capability_fn(capability_fn) > - capability_fn(testpmd_shell) > + capability_fn(testpmd) > for capability in capabilities: > if capability.nic_capability in > supported_capabilities: > > supported_conditional_capabilities.add(capability) > @@ -242,8 +314,8 @@ def get_supported_capabilities( > @classmethod > def _get_decorated_capabilities_map( > cls, > - ) -> dict[TestPmdShellDecorator | None, > set["DecoratedNicCapability"]]: > - capabilities_map: dict[TestPmdShellDecorator | None, > set["DecoratedNicCapability"]] =3D {} > + ) -> dict[TestPmdDecorator | None, set["DecoratedNicCapability"]]: > + capabilities_map: dict[TestPmdDecorator | None, > set["DecoratedNicCapability"]] =3D {} > for capability in cls.capabilities_to_check: > if capability.capability_decorator not in capabilities_map: > capabilities_map[capability.capability_decorator] =3D se= t() > @@ -257,12 +329,12 @@ def _reduce_capabilities( > capabilities: set["DecoratedNicCapability"], > supported_capabilities: MutableSet, > unsupported_capabilities: MutableSet, > - ) -> TestPmdShellMethod: > - def reduced_fn(testpmd_shell: TestPmdShell) -> None: > + ) -> TestPmdMethod: > + def reduced_fn(testpmd: "TestPmd") -> None: > for capability in capabilities: > if capability not in supported_capabilities | > unsupported_capabilities: > capability.capability_fn( > - testpmd_shell, supported_capabilities, > unsupported_capabilities > + testpmd, supported_capabilities, > unsupported_capabilities > ) > > return reduced_fn > @@ -278,11 +350,11 @@ def __repr__(self) -> str: > > @dataclass > class TopologyCapability(Capability): > - """A wrapper around :class:`~.topology.TopologyType`. > + """A wrapper around :class:`~.topology.LinkTopology`. > > Each test case must be assigned a topology. It could be done > explicitly; > - the implicit default is given by > :meth:`~.topology.TopologyType.default`, which this class > - returns :attr:`~.topology.TopologyType.two_links`. > + the implicit default is given by > :meth:`~.topology.LinkTopology.default`, which this class > + returns :attr:`~.topology.LinkTopology.TWO_LINKS`. > > Test case topology may be set by setting the topology for the whole > suite. > The priority in which topology is set is as follows: > @@ -301,7 +373,7 @@ class TopologyCapability(Capability): > topology_type: The topology type that defines each instance. > """ > > - topology_type: TopologyType > + topology_type: LinkTopology > > _unique_capabilities: ClassVar[dict[str, Self]] =3D {} > > @@ -310,7 +382,7 @@ def _preprocess_required(self, test_case_or_suite: > type["TestProtocol"]) -> None > test_case_or_suite.topology_type =3D self > > @classmethod > - def get_unique(cls, topology_type: TopologyType) -> Self: > + def get_unique(cls, topology_type: LinkTopology) -> Self: > """Get the capability uniquely identified by `topology_type`. > > This is a factory method that implements a quasi-enum pattern. > @@ -338,7 +410,7 @@ def get_supported_capabilities( > """Overrides :meth:`~Capability.get_supported_capabilities`.""" > supported_capabilities =3D set() > topology_capability =3D cls.get_unique(topology.type) > - for topology_type in TopologyType: > + for topology_type in LinkTopology: > candidate_topology_type =3D cls.get_unique(topology_type) > if candidate_topology_type <=3D topology_capability: > supported_capabilities.add(candidate_topology_type) > @@ -351,17 +423,17 @@ def set_required(self, test_case_or_suite: > type["TestProtocol"]) -> None: > This means we have to modify test case topologies when processin= g > the test suite topologies. > At that point, the test case topologies have been set by the > :func:`requires` decorator. > The test suite topology only affects the test case topologies > - if not :attr:`~.topology.TopologyType.default`. > + if not :attr:`~.topology.LinkTopology.default`. > > Raises: > ConfigurationError: If the topology type requested by the > test case is more complex than > the test suite's. > """ > if inspect.isclass(test_case_or_suite): > - if self.topology_type is not TopologyType.default(): > + if self.topology_type is not LinkTopology.default(): > self.add_to_required(test_case_or_suite) > for test_case in test_case_or_suite.get_test_cases(): > - if test_case.topology_type.topology_type is > TopologyType.default(): > + if test_case.topology_type.topology_type is > LinkTopology.default(): > # test case topology has not been set, use the > one set by the test suite > self.add_to_required(test_case) > elif test_case.topology_type > > test_case_or_suite.topology_type: > @@ -440,7 +512,7 @@ class TestProtocol(Protocol): > #: The reason for skipping the test case or suite. > skip_reason: ClassVar[str] =3D "" > #: The topology type of the test case or suite. > - topology_type: ClassVar[TopologyCapability] =3D > TopologyCapability(TopologyType.default()) > + topology_type: ClassVar[TopologyCapability] =3D > TopologyCapability(LinkTopology.default()) > #: The capabilities the test case or suite requires in order to be > executed. > required_capabilities: ClassVar[set[Capability]] =3D set() > #: The SUT ports topology configuration of the test case or suite. > @@ -481,7 +553,7 @@ def _decorator(func: type[TestProtocol]) -> > type[TestProtocol]: > > def requires( > *nic_capabilities: NicCapability, > - topology_type: TopologyType =3D TopologyType.default(), > + topology_type: LinkTopology =3D LinkTopology.default(), > ) -> Callable[[type[TestProtocol]], type[TestProtocol]]: > """A decorator that adds the required capabilities to a test case or > test suite. > > diff --git a/dts/framework/testbed_model/linux_session.py > b/dts/framework/testbed_model/linux_session.py > index 604245d855..1f11c3e740 100644 > --- a/dts/framework/testbed_model/linux_session.py > +++ b/dts/framework/testbed_model/linux_session.py > @@ -22,7 +22,7 @@ > InternalError, > RemoteCommandExecutionError, > ) > -from framework.testbed_model.os_session import PortInfo > +from framework.testbed_model.port import PortInfo > from framework.utils import expand_range > > from .cpu import LogicalCore > diff --git a/dts/framework/testbed_model/os_session.py > b/dts/framework/testbed_model/os_session.py > index b6e03aa83d..c1872880da 100644 > --- a/dts/framework/testbed_model/os_session.py > +++ b/dts/framework/testbed_model/os_session.py > @@ -31,13 +31,9 @@ > > from framework.config.node import NodeConfiguration > from framework.logger import DTSLogger > -from framework.remote_session import ( > - InteractiveRemoteSession, > - RemoteSession, > - create_interactive_session, > - create_remote_session, > -) > -from framework.remote_session.remote_session import CommandResult > +from framework.remote_session.interactive_remote_session import > InteractiveRemoteSession > +from framework.remote_session.remote_session import CommandResult, > RemoteSession > +from framework.remote_session.ssh_session import SSHSession > from framework.settings import SETTINGS > from framework.utils import MesonArgs, TarCompressionFormat > > @@ -129,8 +125,8 @@ def __init__( > self._config =3D node_config > self.name =3D name > self._logger =3D logger > - self.remote_session =3D create_remote_session(node_config, name, > logger) > - self.interactive_session =3D > create_interactive_session(node_config, logger) > + self.remote_session =3D SSHSession(node_config, name, logger) > + self.interactive_session =3D InteractiveRemoteSession(node_confi= g, > logger) > > def is_alive(self) -> bool: > """Check whether the underlying remote session is still > responding.""" > diff --git a/dts/framework/testbed_model/topology.py > b/dts/framework/testbed_model/topology.py > index 899ea0ad3a..09303a1f08 100644 > --- a/dts/framework/testbed_model/topology.py > +++ b/dts/framework/testbed_model/topology.py > @@ -11,33 +11,17 @@ > from collections import defaultdict > from collections.abc import Iterator > from dataclasses import dataclass > -from enum import Enum > from typing import Literal, NamedTuple > > from typing_extensions import Self > > +from api.capabilities import LinkTopology > from framework.exception import ConfigurationError, InternalError > from framework.testbed_model.node import Node > > from .port import DriverKind, Port, PortConfig > > > -class TopologyType(int, Enum): > - """Supported topology types.""" > - > - #: A topology with no Traffic Generator. > - no_link =3D 0 > - #: A topology with one physical link between the SUT node and the TG > node. > - one_link =3D 1 > - #: A topology with two physical links between the Sut node and the T= G > node. > - two_links =3D 2 > - > - @classmethod > - def default(cls) -> "TopologyType": > - """The default topology required by test cases if not specified > otherwise.""" > - return cls.two_links > - > - > class PortLink(NamedTuple): > """The physical, cabled connection between the ports.""" > > @@ -71,7 +55,7 @@ class Topology: > tg_ports: The TG ports. > """ > > - type: TopologyType > + type: LinkTopology > sut_ports: list[Port] > tg_ports: list[Port] > pf_ports: list[Port] > @@ -87,15 +71,15 @@ def from_port_links(cls, port_links: > Iterator[PortLink]) -> Self: > Raises: > ConfigurationError: If an unsupported link topology is > supplied. > """ > - type =3D TopologyType.no_link > + type =3D LinkTopology.NO_LINK > > if port_link :=3D next(port_links, None): > - type =3D TopologyType.one_link > + type =3D LinkTopology.ONE_LINK > sut_ports =3D [port_link.sut_port] > tg_ports =3D [port_link.tg_port] > > if port_link :=3D next(port_links, None): > - type =3D TopologyType.two_links > + type =3D LinkTopology.TWO_LINKS > sut_ports.append(port_link.sut_port) > tg_ports.append(port_link.tg_port) > > @@ -268,9 +252,9 @@ def sut_port_ingress(self) -> Port: > @property > def sut_port_egress(self) -> Port: > """The egress port of the SUT node.""" > - return self.sut_ports[1 if self.type is TopologyType.two_links > else 0] > + return self.sut_ports[1 if self.type is LinkTopology.TWO_LINKS > else 0] > > @property > def tg_port_ingress(self) -> Port: > """The ingress port of the TG node.""" > - return self.tg_ports[1 if self.type is TopologyType.two_links > else 0] > + return self.tg_ports[1 if self.type is LinkTopology.TWO_LINKS > else 0] > -- > 2.39.5 > > --000000000000d23d9c063f2ccb36 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
This series has been=C2=A0applied to next-dts.=C2=A0
<= br>
I am noting here that patchwork CI results shows a checkpatch= es=C2=A0warning. However, I have run checkpatches=C2=A0on this patch and it= 's reporting no issues. My=C2=A0DPDK_CHECKPATCH_PATH and codespell dict= ionary seem fine. So, I am confident enough that there is no issue to apply= to next-dts. Ali please let me know if you are concerned.

<= div class=3D"gmail_quote gmail_quote_container">
On Fri, Aug 29, 2025 at 1:43=E2=80=AFPM Paul Szczepanek <paul.szczepanek@arm.com> wrot= e:
Testpmd moved= into new API directory.
Capabilities converted into vanilla enum and moved to API.
Removed some ciruclar dependencies and incorrect imports.

Signed-off-by: Paul Szczepanek <paul.szczepanek@arm.com>
---
=C2=A0doc/api/dts/api.capabilities.rst=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 |=C2=A0 =C2=A0 8 +
=C2=A0doc/api/dts/api.rst=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A020 +
=C2=A0...stpmd_shell.rst =3D> api.testpmd.config.rst} |=C2=A0 =C2=A0 4 += -
=C2=A0doc/api/dts/api.testpmd.rst=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A015 +
=C2=A0doc/api/dts/api.testpmd.types.rst=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0|=C2=A0 =C2=A0 8 +
=C2=A0doc/api/dts/framework.params.rst=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 |=C2=A0 =C2=A0 1 -
=C2=A0doc/api/dts/framework.params.testpmd.rst=C2=A0 =C2=A0 =C2=A0 |=C2=A0 = =C2=A0 8 -
=C2=A0doc/api/dts/framework.remote_session.rst=C2=A0 =C2=A0 =C2=A0 |=C2=A0 = =C2=A0 1 -
=C2=A0doc/api/dts/index.rst=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A0 1 +
=C2=A0dts/api/__init__.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A014 +
=C2=A0dts/api/capabilities.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 180 ++
=C2=A0dts/api/testpmd/__init__.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0| 1294 ++++++++
=C2=A0.../testpmd.py =3D> api/testpmd/config.py}=C2=A0 =C2=A0 =C2=A0 |= =C2=A0 =C2=A0 9 +-
=C2=A0dts/api/testpmd/types.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 1406 ++++++++
=C2=A0dts/framework/config/__init__.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 |=C2=A0 =C2=A0 3 +-
=C2=A0dts/framework/params/eal.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A012 +-
=C2=A0dts/framework/params/types.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A0 4 +-
=C2=A0dts/framework/remote_session/__init__.py=C2=A0 =C2=A0 =C2=A0 |=C2=A0 = =C2=A044 -
=C2=A0dts/framework/remote_session/testpmd_shell.py | 2844 ----------------= -
=C2=A0dts/framework/testbed_model/capability.py=C2=A0 =C2=A0 =C2=A0|=C2=A0 = 144 +-
=C2=A0dts/framework/testbed_model/linux_session.py=C2=A0 |=C2=A0 =C2=A0 2 += -
=C2=A0dts/framework/testbed_model/os_session.py=C2=A0 =C2=A0 =C2=A0|=C2=A0 = =C2=A014 +-
=C2=A0dts/framework/testbed_model/topology.py=C2=A0 =C2=A0 =C2=A0 =C2=A0|= =C2=A0 =C2=A030 +-
=C2=A023 files changed, 3086 insertions(+), 2980 deletions(-)
=C2=A0create mode 100644 doc/api/dts/api.capabilities.rst
=C2=A0create mode 100644 doc/api/dts/api.rst
=C2=A0rename doc/api/dts/{framework.remote_session.testpmd_shell.rst =3D>= ; api.testpmd.config.rst} (54%)
=C2=A0create mode 100644 doc/api/dts/api.testpmd.rst
=C2=A0create mode 100644 doc/api/dts/api.testpmd.types.rst
=C2=A0delete mode 100644 doc/api/dts/framework.params.testpmd.rst
=C2=A0create mode 100644 dts/api/__init__.py
=C2=A0create mode 100644 dts/api/capabilities.py
=C2=A0create mode 100644 dts/api/testpmd/__init__.py
=C2=A0rename dts/{framework/params/testpmd.py =3D> api/testpmd/config.py= } (98%)
=C2=A0create mode 100644 dts/api/testpmd/types.py
=C2=A0delete mode 100644 dts/framework/remote_session/testpmd_shell.py

diff --git a/doc/api/dts/api.capabilities.rst b/doc/api/dts/api.capabilitie= s.rst
new file mode 100644
index 0000000000..311872f61d
--- /dev/null
+++ b/doc/api/dts/api.capabilities.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+capabilities - SUT Capabilities
+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
+
+.. automodule:: api.capabilities
+=C2=A0 =C2=A0:members:
+=C2=A0 =C2=A0:show-inheritance:
diff --git a/doc/api/dts/api.rst b/doc/api/dts/api.rst
new file mode 100644
index 0000000000..d3cf1226eb
--- /dev/null
+++ b/doc/api/dts/api.rst
@@ -0,0 +1,20 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+api - DTS API
+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
+
+.. automodule:: api
+=C2=A0 =C2=A0:members:
+=C2=A0 =C2=A0:show-inheritance:
+
+.. toctree::
+=C2=A0 =C2=A0:hidden:
+=C2=A0 =C2=A0:maxdepth: 2
+
+=C2=A0 =C2=A0api.testpmd
+
+.. toctree::
+=C2=A0 =C2=A0:hidden:
+=C2=A0 =C2=A0:maxdepth: 1
+
+=C2=A0 =C2=A0api.capabilities
\ No newline at end of file
diff --git a/doc/api/dts/framework.remote_session.testpmd_shell.rst b/doc/a= pi/dts/api.testpmd.config.rst
similarity index 54%
rename from doc/api/dts/framework.remote_session.testpmd_shell.rst
rename to doc/api/dts/api.testpmd.config.rst
index 81ca23337f..d338c07a36 100644
--- a/doc/api/dts/framework.remote_session.testpmd_shell.rst
+++ b/doc/api/dts/api.testpmd.config.rst
@@ -1,8 +1,8 @@
=C2=A0.. SPDX-License-Identifier: BSD-3-Clause

-testpmd\_shell - Testpmd Interactive Remote Shell
+config - Testpmd configuration
=C2=A0=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D

-.. automodule:: framework.remote_session.testpmd_shell
+.. automodule:: api.testpmd.config
=C2=A0 =C2=A0 :members:
=C2=A0 =C2=A0 :show-inheritance:
diff --git a/doc/api/dts/api.testpmd.rst b/doc/api/dts/api.testpmd.rst
new file mode 100644
index 0000000000..75a92823f8
--- /dev/null
+++ b/doc/api/dts/api.testpmd.rst
@@ -0,0 +1,15 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+testpmd - Testpmd Interactive Remote Shell
+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
+
+.. automodule:: api.testpmd
+=C2=A0 =C2=A0:members:
+=C2=A0 =C2=A0:show-inheritance:
+
+.. toctree::
+=C2=A0 =C2=A0:hidden:
+=C2=A0 =C2=A0:maxdepth: 1
+
+=C2=A0 =C2=A0api.testpmd.types
+=C2=A0 =C2=A0api.testpmd.config
\ No newline at end of file
diff --git a/doc/api/dts/api.testpmd.types.rst b/doc/api/dts/api.testpmd.ty= pes.rst
new file mode 100644
index 0000000000..75b197aa73
--- /dev/null
+++ b/doc/api/dts/api.testpmd.types.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+types - Testpmd types
+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
+
+.. automodule:: api.testpmd.types
+=C2=A0 =C2=A0:members:
+=C2=A0 =C2=A0:show-inheritance:
diff --git a/doc/api/dts/framework.params.rst b/doc/api/dts/framework.param= s.rst
index 4e263e2e5c..d8c6af9667 100644
--- a/doc/api/dts/framework.params.rst
+++ b/doc/api/dts/framework.params.rst
@@ -12,5 +12,4 @@ params - Command Line Parameters Modelling
=C2=A0 =C2=A0 :maxdepth: 1

=C2=A0 =C2=A0 framework.params.eal
-=C2=A0 =C2=A0framework.params.testpmd
=C2=A0 =C2=A0 framework.params.types
diff --git a/doc/api/dts/framework.params.testpmd.rst b/doc/api/dts/framewo= rk.params.testpmd.rst
deleted file mode 100644
index 19583b01de..0000000000
--- a/doc/api/dts/framework.params.testpmd.rst
+++ /dev/null
@@ -1,8 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-testpmd - TestPMD Parameters Modelling
-=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
-
-.. automodule:: framework.params.testpmd
-=C2=A0 =C2=A0:members:
-=C2=A0 =C2=A0:show-inheritance:
diff --git a/doc/api/dts/framework.remote_session.rst b/doc/api/dts/framewo= rk.remote_session.rst
index 27c9153e64..b7dbe71412 100644
--- a/doc/api/dts/framework.remote_session.rst
+++ b/doc/api/dts/framework.remote_session.rst
@@ -18,5 +18,4 @@ remote\_session - Node Connections Package
=C2=A0 =C2=A0 framework.remote_session.shell_pool
=C2=A0 =C2=A0 framework.remote_session.dpdk
=C2=A0 =C2=A0 framework.remote_session.dpdk_shell
-=C2=A0 =C2=A0framework.remote_session.testpmd_shell
=C2=A0 =C2=A0 framework.remote_session.python_shell
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index a11f395e11..c719297c11 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -15,6 +15,7 @@ Packages
=C2=A0 =C2=A0 :maxdepth: 1

=C2=A0 =C2=A0 tests
+=C2=A0 =C2=A0api
=C2=A0 =C2=A0 framework.testbed_model
=C2=A0 =C2=A0 framework.remote_session
=C2=A0 =C2=A0 framework.params
diff --git a/dts/api/__init__.py b/dts/api/__init__.py
new file mode 100644
index 0000000000..26b773bfbb
--- /dev/null
+++ b/dts/api/__init__.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 Arm Limited
+
+"""DTS API.
+
+This package exposes public API modules for test writers.
+
+All modules in this package are considered stable. Do not use framework +internal modules in your tests. Any missing functionality should be added<= br> +to the public API.
+
+Private methods and members are prefixed with an underscore and should not= be
+used outside of the framework.
+"""
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
new file mode 100644
index 0000000000..1a79413f6f
--- /dev/null
+++ b/dts/api/capabilities.py
@@ -0,0 +1,180 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 PANTHEON.tech s.r.o.
+# Copyright(c) 2025 Arm Limited
+
+"""Testbed capabilities.
+
+This module provides a protocol that defines the common attributes of test= cases and suites
+and support for test environment capabilities.
+
+Many test cases are testing features not available on all hardware.
+On the other hand, some test cases or suites may not need the most complex= topology available.
+
+The module allows developers to mark test cases or suites to require certa= in hardware capabilities
+or a particular topology.
+
+There are differences between hardware and topology capabilities:
+
+=C2=A0 =C2=A0 * Hardware capabilities are assumed to not be required when = not specified.
+=C2=A0 =C2=A0 * However, some topology is always available, so each test c= ase or suite is assigned
+=C2=A0 =C2=A0 =C2=A0 a default topology if no topology is specified in the= decorator.
+
+Examples:
+=C2=A0 =C2=A0 .. code:: python
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 from framework.test_suite import TestSuite, fu= nc_test
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 from framework.testbed_model.capability import= LinkTopology, requires_link_topology
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # The whole test suite (each test case within)= doesn't require any links.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 @requires_link_topology(LinkTopology.NO_LINK)<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 @func_test
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 class TestHelloWorld(TestSuite):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 def hello_world_single_core(self= ):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ...
+
+=C2=A0 =C2=A0 .. code:: python
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 from framework.test_suite import TestSuite, fu= nc_test
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 from framework.testbed_model.capability import= NicCapability, requires_nic_capability
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 class TestPmdBufferScatter(TestSuite):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # only the test case requires th= e SCATTERED_RX_ENABLED capability
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # other test cases may not requi= re it
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 @requires_nic_capability(NicCapa= bility.SCATTERED_RX_ENABLED)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 @func_test
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 def test_scatter_mbuf_2048(self)= :
+"""
+
+from enum import IntEnum, auto
+from typing import TYPE_CHECKING, Callable
+
+if TYPE_CHECKING:
+=C2=A0 =C2=A0 from framework.test_suite import TestProtocol
+
+
+class LinkTopology(IntEnum):
+=C2=A0 =C2=A0 """Supported topology types."""= ;
+
+=C2=A0 =C2=A0 #: A topology with no Traffic Generator.
+=C2=A0 =C2=A0 NO_LINK =3D 0
+=C2=A0 =C2=A0 #: A topology with one physical link between the SUT node an= d the TG node.
+=C2=A0 =C2=A0 ONE_LINK =3D auto()
+=C2=A0 =C2=A0 #: A topology with two physical links between the Sut node a= nd the TG node.
+=C2=A0 =C2=A0 TWO_LINKS =3D auto()
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def default(cls) -> "LinkTopology":
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """The default topology require= d by test cases if not specified otherwise."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return cls.TWO_LINKS
+
+
+class NicCapability(IntEnum):
+=C2=A0 =C2=A0 """DPDK NIC capabilities.
+
+=C2=A0 =C2=A0 The capabilities are used to mark test cases or suites that = require a specific
+=C2=A0 =C2=A0 DPDK NIC capability to run. The capabilities are used by the= test framework to
+=C2=A0 =C2=A0 determine whether a test case or suite can be run on the cur= rent testbed.
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #: Scattered packets Rx enabled.
+=C2=A0 =C2=A0 SCATTERED_RX_ENABLED =3D 0
+=C2=A0 =C2=A0 #: Device supports VLAN stripping.
+=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_STRIP =3D auto()
+=C2=A0 =C2=A0 #: Device supports L3 checksum offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_IPV4_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_UDP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_TCP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports Large Receive Offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_TCP_LRO =3D auto()
+=C2=A0 =C2=A0 #: Device supports QinQ (queue in queue) offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_QINQ_STRIP =3D auto()
+=C2=A0 =C2=A0 #: Device supports inner packet L3 checksum.
+=C2=A0 =C2=A0 RX_OFFLOAD_OUTER_IPV4_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports MACsec.
+=C2=A0 =C2=A0 RX_OFFLOAD_MACSEC_STRIP =3D auto()
+=C2=A0 =C2=A0 #: Device supports filtering of a VLAN Tag identifier.
+=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_FILTER =3D auto()
+=C2=A0 =C2=A0 #: Device supports VLAN offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_EXTEND =3D auto()
+=C2=A0 =C2=A0 #: Device supports receiving segmented mbufs.
+=C2=A0 =C2=A0 RX_OFFLOAD_SCATTER =3D auto()
+=C2=A0 =C2=A0 #: Device supports Timestamp.
+=C2=A0 =C2=A0 RX_OFFLOAD_TIMESTAMP =3D auto()
+=C2=A0 =C2=A0 #: Device supports crypto processing while packet is receive= d in NIC.
+=C2=A0 =C2=A0 RX_OFFLOAD_SECURITY =3D auto()
+=C2=A0 =C2=A0 #: Device supports CRC stripping.
+=C2=A0 =C2=A0 RX_OFFLOAD_KEEP_CRC =3D auto()
+=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_SCTP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports inner packet L4 checksum.
+=C2=A0 =C2=A0 RX_OFFLOAD_OUTER_UDP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports RSS hashing.
+=C2=A0 =C2=A0 RX_OFFLOAD_RSS_HASH =3D auto()
+=C2=A0 =C2=A0 #: Device supports scatter Rx packets to segmented mbufs. +=C2=A0 =C2=A0 RX_OFFLOAD_BUFFER_SPLIT =3D auto()
+=C2=A0 =C2=A0 #: Device supports all checksum capabilities.
+=C2=A0 =C2=A0 RX_OFFLOAD_CHECKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports all VLAN capabilities.
+=C2=A0 =C2=A0 RX_OFFLOAD_VLAN =3D auto()
+=C2=A0 =C2=A0 #: Device supports Rx queue setup after device started.
+=C2=A0 =C2=A0 RUNTIME_RX_QUEUE_SETUP =3D auto()
+=C2=A0 =C2=A0 #: Device supports Tx queue setup after device started.
+=C2=A0 =C2=A0 RUNTIME_TX_QUEUE_SETUP =3D auto()
+=C2=A0 =C2=A0 #: Device supports shared Rx queue among ports within Rx dom= ain and switch domain.
+=C2=A0 =C2=A0 RXQ_SHARE =3D auto()
+=C2=A0 =C2=A0 #: Device supports keeping flow rules across restart.
+=C2=A0 =C2=A0 FLOW_RULE_KEEP =3D auto()
+=C2=A0 =C2=A0 #: Device supports keeping shared flow objects across restar= t.
+=C2=A0 =C2=A0 FLOW_SHARED_OBJECT_KEEP =3D auto()
+=C2=A0 =C2=A0 #: Device supports multicast address filtering.
+=C2=A0 =C2=A0 MCAST_FILTERING =3D auto()
+=C2=A0 =C2=A0 #: Device supports flow ctrl.
+=C2=A0 =C2=A0 FLOW_CTRL =3D auto()
+=C2=A0 =C2=A0 #: Device is running on a physical function.
+=C2=A0 =C2=A0 PHYSICAL_FUNCTION =3D auto()
+
+
+def requires_link_topology(
+=C2=A0 =C2=A0 link_topology: LinkTopology,
+) -> Callable[[type["TestProtocol"]], type["TestProtocol= "]]:
+=C2=A0 =C2=A0 """Decorator to set required topology type fo= r a test case or test suite.
+
+=C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 link_topology: The topology type the test suit= e or case requires.
+
+=C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The decorated test case or test suite.
+=C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 from framework.testbed_model.capability import TopologyCapab= ility
+
+=C2=A0 =C2=A0 def add_required_topology(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 test_case_or_suite: type["TestProtocol&qu= ot;],
+=C2=A0 =C2=A0 ) -> type["TestProtocol"]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 topology_capability =3D TopologyCapability.get= _unique(link_topology)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 topology_capability.set_required(test_case_or_= suite)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return test_case_or_suite
+
+=C2=A0 =C2=A0 return add_required_topology
+
+
+def requires_nic_capability(
+=C2=A0 =C2=A0 nic_capability: NicCapability,
+) -> Callable[[type["TestProtocol"]], type["TestProtocol= "]]:
+=C2=A0 =C2=A0 """Decorator to add a single required NIC cap= ability to a test case or test suite.
+
+=C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 nic_capability: The NIC capability that is req= uired by the test case or test suite.
+
+=C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The decorated test case or test suite.
+=C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 from framework.testbed_model.capability import DecoratedNicC= apability
+
+=C2=A0 =C2=A0 def add_required_capability(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 test_case_or_suite: type["TestProtocol&qu= ot;],
+=C2=A0 =C2=A0 ) -> type["TestProtocol"]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 decorated =3D DecoratedNicCapability.get_uniqu= e(nic_capability)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 decorated.add_to_required(test_case_or_suite)<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 return test_case_or_suite
+
+=C2=A0 =C2=A0 return add_required_capability
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
new file mode 100644
index 0000000000..49cf3742dd
--- /dev/null
+++ b/dts/api/testpmd/__init__.py
@@ -0,0 +1,1294 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 University of New Hampshire
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2024 Arm Limited
+
+"""Testpmd interactive shell.
+
+Typical usage example in a TestSuite::
+
+=C2=A0 =C2=A0 testpmd =3D TestPmd(self.sut_node)
+=C2=A0 =C2=A0 devices =3D testpmd.get_devices()
+=C2=A0 =C2=A0 for device in devices:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 print(device)
+=C2=A0 =C2=A0 testpmd.close()
+"""
+
+import functools
+import re
+import time
+from collections.abc import MutableSet
+from enum import Flag
+from pathlib import PurePath
+from typing import (
+=C2=A0 =C2=A0 Any,
+=C2=A0 =C2=A0 Callable,
+=C2=A0 =C2=A0 ClassVar,
+=C2=A0 =C2=A0 Concatenate,
+=C2=A0 =C2=A0 ParamSpec,
+=C2=A0 =C2=A0 Tuple,
+)
+
+from typing_extensions import Unpack
+
+from api.capabilities import LinkTopology, NicCapability
+from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPm= dParams
+from api.testpmd.types import (
+=C2=A0 =C2=A0 ChecksumOffloadOptions,
+=C2=A0 =C2=A0 DeviceCapabilitiesFlag,
+=C2=A0 =C2=A0 FlowRule,
+=C2=A0 =C2=A0 RxOffloadCapabilities,
+=C2=A0 =C2=A0 RxOffloadCapability,
+=C2=A0 =C2=A0 TestPmdDevice,
+=C2=A0 =C2=A0 TestPmdPort,
+=C2=A0 =C2=A0 TestPmdPortFlowCtrl,
+=C2=A0 =C2=A0 TestPmdPortStats,
+=C2=A0 =C2=A0 TestPmdQueueInfo,
+=C2=A0 =C2=A0 TestPmdRxqInfo,
+=C2=A0 =C2=A0 TestPmdVerbosePacket,
+=C2=A0 =C2=A0 VLANOffloadFlag,
+)
+from framework.context import get_ctx
+from framework.exception import InteractiveCommandExecutionError, Internal= Error
+from framework.params.types import TestPmdParamsDict
+from framework.remote_session.dpdk_shell import DPDKShell
+from framework.remote_session.interactive_shell import only_active
+from framework.settings import SETTINGS
+
+P =3D ParamSpec("P")
+TestPmdMethod =3D Callable[Concatenate["TestPmd", P], Any]
+
+
+def _requires_stopped_ports(func: TestPmdMethod) -> TestPmdMethod:
+=C2=A0 =C2=A0 """Decorator for :class:`TestPmd` commands me= thods that require stopped ports.
+
+=C2=A0 =C2=A0 If the decorated method is called while the ports are starte= d, then these are stopped before
+=C2=A0 =C2=A0 continuing.
+
+=C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 func: The :class:`TestPmd` method to decorate.=
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 @functools.wraps(func)
+=C2=A0 =C2=A0 def _wrapper(self: "TestPmd", *args: P.args, **kwa= rgs: P.kwargs):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.ports_started:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Ports n= eed to be stopped to continue.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.stop_all_ports()
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return func(self, *args, **kwargs)
+
+=C2=A0 =C2=A0 return _wrapper
+
+
+def _requires_started_ports(func: TestPmdMethod) -> TestPmdMethod:
+=C2=A0 =C2=A0 """Decorator for :class:`TestPmd` commands me= thods that require started ports.
+
+=C2=A0 =C2=A0 If the decorated method is called while the ports are stoppe= d, then these are started before
+=C2=A0 =C2=A0 continuing.
+
+=C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 func: The :class:`TestPmd` method to decorate.=
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 @functools.wraps(func)
+=C2=A0 =C2=A0 def _wrapper(self: "TestPmd", *args: P.args, **kwa= rgs: P.kwargs):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not self.ports_started:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Ports n= eed to be started to continue.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.start_all_ports()
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return func(self, *args, **kwargs)
+
+=C2=A0 =C2=A0 return _wrapper
+
+
+def _add_remove_mtu(mtu: int =3D 1500) -> Callable[[TestPmdMethod], Tes= tPmdMethod]:
+=C2=A0 =C2=A0 """Configure MTU to `mtu` on all ports, run t= he decorated function, then revert.
+
+=C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mtu: The MTU to configure all ports on.
+
+=C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The method decorated with setting and revertin= g MTU.
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 def decorator(func: TestPmdMethod) -> TestPmdMethod:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 @functools.wraps(func)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 def wrapper(self: "TestPmd", *args: = P.args, **kwargs: P.kwargs):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 original_mtu =3D self.ports[0].m= tu
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.set_port_mtu_all(mtu=3Dmtu,= verify=3DFalse)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 retval =3D func(self, *args, **k= wargs)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.set_port_mtu_all(original_m= tu if original_mtu else 1500, verify=3DFalse)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return retval
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return wrapper
+
+=C2=A0 =C2=A0 return decorator
+
+
+class TestPmd(DPDKShell):
+=C2=A0 =C2=A0 """Testpmd interactive shell.
+
+=C2=A0 =C2=A0 The testpmd shell users should never use
+=C2=A0 =C2=A0 the :meth:`~.interactive_shell.InteractiveShell.send_command= ` method directly, but rather
+=C2=A0 =C2=A0 call specialized methods. If there isn't one that satisf= ies a need, it should be added.
+
+=C2=A0 =C2=A0 Attributes:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ports_started: Indicates whether the ports are= started.
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 _app_params: TestPmdParams
+=C2=A0 =C2=A0 _ports: list[TestPmdPort] | None
+
+=C2=A0 =C2=A0 #: The testpmd's prompt.
+=C2=A0 =C2=A0 _default_prompt: ClassVar[str] =3D "testpmd>" +
+=C2=A0 =C2=A0 #: This forces the prompt to appear after sending a command.=
+=C2=A0 =C2=A0 _command_extra_chars: ClassVar[str] =3D "\n"
+
+=C2=A0 =C2=A0 ports_started: bool
+
+=C2=A0 =C2=A0 def __init__(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 name: str | None =3D None,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 privileged: bool =3D True,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 **app_params: Unpack[TestPmdParamsDict],
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Overrides :meth:`~.dpdk_shel= l.DPDKShell.__init__`. Changes app_params to kwargs."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "port_topology" not in app_params= and get_ctx().topology.type is LinkTopology.ONE_LINK:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 app_params["port_topology&q= uot;] =3D PortTopology.loop
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 super().__init__(name, privileged, app_params= =3DTestPmdParams(**app_params))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.ports_started =3D not self._app_params.di= sable_device_start
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._ports =3D None
+
+=C2=A0 =C2=A0 @property
+=C2=A0 =C2=A0 def path(self) -> PurePath:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """The path to the testpmd exec= utable."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return PurePath("app/dpdk-testpmd")<= br> +
+=C2=A0 =C2=A0 @property
+=C2=A0 =C2=A0 def ports(self) -> list[TestPmdPort]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """The ports of the instance. +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 This caches the ports returned by :meth:`show_= port_info_all`.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 To force an update of port information, execut= e :meth:`show_port_info_all` or
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 :meth:`show_port_info`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns: The list of known testpmd ports.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self._ports is None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return self.show_port_info_all()=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self._ports
+
+=C2=A0 =C2=A0 @_requires_started_ports
+=C2=A0 =C2=A0 def start(self, verify: bool =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Start packet forwarding with= the current configuration.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` , a seco= nd start command will be sent in an attempt to verify
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 packet forwarding = started as expected.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and forwarding fails to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 start or ports fai= l to come up.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command("start")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # If forwarding was already star= ted, sending "start" again should tell us
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 start_cmd_output =3D self.send_c= ommand("start")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Packet forwarding alrea= dy started" not in start_cmd_output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to start packet forwarding: \n{start_cmd_output}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError("Testpmd failed to start packet forwarding."= )
+
+=C2=A0 =C2=A0 def stop(self, verify: bool =3D True) -> str:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Stop packet forwarding.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` , the ou= tput of the stop command is scanned to verify that
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 forwarding was sto= pped successfully or not started. If neither is found, it is
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 considered an erro= r.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the command to stop
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 forwarding results= in an error.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Output gathered from the stop co= mmand and all other preceding logs in the buffer. This
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 output is most often used to vie= w forwarding statistics that are displayed when this
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 command is sent as well as any v= erbose packet information that hasn't been consumed
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 prior to calling this method. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 stop_cmd_output =3D self.send_command("st= op")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "Done." = not in stop_cmd_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 and "Packet f= orwarding not started" not in stop_cmd_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to stop packet forwarding: \n{stop_cmd_output}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError("Testpmd failed to stop packet forwarding.")=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return stop_cmd_output
+
+=C2=A0 =C2=A0 def get_devices(self) -> list[TestPmdDevice]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get a list of device names t= hat are known to testpmd.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Uses the device info listed in testpmd and the= n parses the output.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 A list of devices.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_info: str =3D self.send_command("show= device info all")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_list: list[TestPmdDevice] =3D []
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for line in dev_info.split("\n"): +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "device name:" in l= ine.lower():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_list.append(Te= stPmdDevice(line))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return dev_list
+
+=C2=A0 =C2=A0 def wait_link_status_up(self, port_id: int, timeout=3DSETTIN= GS.timeout) -> bool:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Wait until the link status o= n the given port is "up".
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Arguments:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: Port to check the link = status on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 timeout: Time to wait for the li= nk to come up. The default value for this
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 argument may be mo= dified using the :option:`--timeout` command-line argument
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or the :envvar:`DT= S_TIMEOUT` environment variable.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Whether the link came up in time= or not.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 time_to_stop =3D time.time() + timeout
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_info: str =3D ""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 while time.time() < time_to_stop:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_info =3D self.send_command(= f"show port info {port_id}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Link status: up" i= n port_info:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 time.sleep(0.5)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.error(f"The li= nk for port {port_id} did not come up in the given timeout.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return "Link status: up" in port_inf= o
+
+=C2=A0 =C2=A0 def set_forward_mode(self, mode: SimpleForwardingModes, veri= fy: bool =3D True):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set packet forwarding mode.<= br> +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode: The forwarding mode to use= .
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the command will be scanned in an attempt to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify that the fo= rwarding mode was set to `mode` properly.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the forwarding mode
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to update. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 set_fwd_output =3D self.send_command(f"se= t fwd {mode.value}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if f"Set {mode.value} packe= t forwarding mode" not in set_fwd_output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to set fwd mode to {mode.value}:\n{set_fwd_output}") +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Test pmd failed to set fwd mode to {mode.value}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def stop_all_ports(self, verify: bool =3D True) -> None:<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Stops all the ports.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command will be checked for a successful
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 execution.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the ports were not
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stopped successful= ly.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Stopping all the port= s...")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command("port stop a= ll")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify and not output.strip().endswith(&quo= t;Done"):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("Ports were not stopped successfully.")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.ports_started =3D False
+
+=C2=A0 =C2=A0 def start_all_ports(self, verify: bool =3D True) -> None:=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Starts all the ports.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command will be checked for a successful
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 execution.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the ports were not
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 started successful= ly.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Starting all the port= s...")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command("port start = all")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify and not output.strip().endswith(&quo= t;Done"):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("Ports were not started successfully.")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.ports_started =3D True
+
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def set_ports_queues(self, number_of: int) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Sets the number of queues pe= r port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 number_of: The number of RX/TX q= ueues to create per port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InternalError: If `number_of` is= invalid.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if number_of < 1:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InternalError("The nu= mber of queues must be positive and non-zero.")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port config all rxq {= number_of}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port config all txq {= number_of}")
+
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def close_all_ports(self, verify: bool =3D True) -> None:=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Close all ports.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the close command will be scanned in an attempt
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 to verify that all= ports were stopped successfully. Defaults to :data:`True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and at lease one port
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 failed to close. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_close_output =3D self.send_command("= port close all")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 num_ports =3D len(self.ports) +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if not all(f"Port {p_id} is= closed" in port_close_output for p_id in range(num_ports)):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError("Ports were not closed successfully.")
+
+=C2=A0 =C2=A0 def show_port_info_all(self) -> list[TestPmdPort]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the information of a= ll the ports.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 list[TestPmdPort]: A list contai= ning all the ports information as `TestPmdPort`.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command("show port i= nfo all")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Sample output of the "all" command= looks like:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # <start>
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0********************* Infos for = port 0 *********************
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0Key: value
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0********************* Infos for = port 1 *********************
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0Key: value
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # <end>
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Takes advantage of the double new line in be= tween ports as end delimiter. But we need to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # artificially add a new line at the end to pi= ck up the last port. Because commands are
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # executed on a pseudo-terminal created by par= amiko on the remote node, lines end with CRLF.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Therefore we also need to take the carriage = return into account.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 iter =3D re.finditer(r"\*{21}.*?[\r\n]{4}= ", output + "\r\n", re.S)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._ports =3D [TestPmdPort.parse(block.group= (0)) for block in iter]
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self._ports
+
+=C2=A0 =C2=A0 def show_port_info(self, port_id: int) -> TestPmdPort: +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the given port infor= mation.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port ID to gather i= nformation for.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `port_id` is invalid.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdPort: An instance of `Tes= tPmdPort` containing the given port's information.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"show port = info {port_id}", skip_first_line=3DTrue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if output.startswith("Invalid port")= :
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("invalid port given")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 port =3D TestPmdPort.parse(output)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._update_port(port)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return port
+
+=C2=A0 =C2=A0 def _update_port(self, port: TestPmdPort) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self._ports:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._ports =3D [
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 existing_port if <= a href=3D"http://port.id" rel=3D"noreferrer" target=3D"_blank">port.id = !=3D existing_port.id else port
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for existing_port = in self._ports
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ]
+
+=C2=A0 =C2=A0 def set_mac_addr(self, port_id: int, mac_address: str, add: = bool, verify: bool =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add or remove a mac address = on a given port's Allowlist.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port ID the mac add= ress is set on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mac_address: The mac address to = be added to or removed from the specified port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add: If :data:`True`, add the sp= ecified mac address. If :data:`False`, remove specified
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mac address.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:'True',= assert that the 'mac_addr' operation was successful. If
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 :data:'False&#= 39;, run the command and skip this assertion.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If the set mac address operation fails.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mac_cmd =3D "add" if add else "= remove"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"mac_addr {= mac_cmd} {port_id} {mac_address}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Bad arguments" in output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Invalid= argument provided to mac_addr")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("Invalid argument provided")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "mac_addr_cmd error:&quo= t; in output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to {mac_cmd} {mac_address} on port {port_id}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to {mac_cmd} {mac_address} on port {port_id} \n{output}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def set_multicast_mac_addr(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, multi_addr: str, add: bool= , verify: bool =3D True
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add or remove multicast mac = address to a specified port's allow list.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port ID the multica= st address is set on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 multi_addr: The multicast addres= s to be added or removed from the filter.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add: If :data:'True', ad= d the specified multicast address to the port filter.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 If :data:'Fals= e', remove the specified multicast address from the port filter.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:'True',= assert that the 'mcast_addr' operations was successful.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 If :data:'Fals= e', execute the 'mcast_addr' operation and skip the assertion.<= br> +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If either the 'add' or 'remove' operations fails.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mcast_cmd =3D "add" if add else &quo= t;remove"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"mcast_addr= {mcast_cmd} {port_id} {multi_addr}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Bad arguments" in output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Invalid= arguments provided to mcast_addr")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("Invalid argument provided")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "Invalid mult= icast_addr" in output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or f"multicas= t address {'already' if add else 'not'} filtered by port&qu= ot; in output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to {mcast_cmd} {multi_addr} on port {port_id}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to {mcast_cmd} {multi_addr} on port {port_id} \n{output}" +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def show_port_stats_all(self) -> Tuple[list[TestPmdPortSt= ats], str]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the statistics of al= l the ports.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Tuple[str, list[TestPmdPortStats= ]]: A tuple where the first element is the stats of all
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ports as `TestPmdPortStats` and = second is the raw testpmd output that was collected
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 from the sent command.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command("show port s= tats all")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Sample output of the "all" command= looks like:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0########### NIC statistics for p= ort 0 ###########
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0values...
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0################################= #################
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0########### NIC statistics for p= ort 1 ###########
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0values...
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0################################= #################
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 iter =3D re.finditer(r"(^=C2=A0 #*.+#*$[^= #]+)^=C2=A0 #*\r$", output, re.MULTILINE)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return ([TestPmdPortStats.parse(block.group(1)= ) for block in iter], output)
+
+=C2=A0 =C2=A0 def show_port_stats(self, port_id: int) -> TestPmdPortSta= ts:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the given port stati= stics.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port ID to gather i= nformation for.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `port_id` is invalid.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdPortStats: An instance of= `TestPmdPortStats` containing the given port's stats.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"show port = stats {port_id}", skip_first_line=3DTrue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if output.startswith("Invalid port")= :
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("invalid port given")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TestPmdPortStats.parse(output)
+
+=C2=A0 =C2=A0 def set_multicast_all(self, on: bool, verify: bool =3D True)= -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Turns multicast mode on/off = for the specified port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 on: If :data:`True`, turns multi= cast mode on, otherwise turns off.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` an addit= ional command will be sent to verify
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 that multicast mod= e is properly set. Defaults to :data:`True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and multicast
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode is not proper= ly set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 multicast_cmd_output =3D self.send_command(f&q= uot;set allmulti all {'on' if on else 'off'}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_stats =3D self.show_port_in= fo_all()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if on ^ all(stats.is_allmulticas= t_mode_enabled for stats in port_stats):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to set multicast mode on all ports.: \n{multicast_cmd_output}&quo= t;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &quo= t;Testpmd failed to set multicast mode on all ports."
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def csum_set_hw(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, layers: ChecksumOffloadOptions, port_id:= int, verify: bool =3D True
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Enables hardware checksum of= floading on the specified layer.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 layers: The layer/layers that ch= ecksum offloading should be enabled on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port number to enab= le checksum offloading on, should be within 0-32.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the command will be scanned in an attempt to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify that checks= um offloading was enabled on the port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If checksum offload is not enabled successfully.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name, offload in ChecksumOffloadOptions.__= members__.items():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if offload in layers:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name =3D name.repl= ace("_", "-")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 csum_output =3D se= lf.send_command(f"csum set {name} hw {port_id}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 "Bad arguments" in csum_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 or f"Please stop port {port_id} first" in csum_output<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 or f"checksum offload is not supported by port {port_id}&qu= ot; in csum_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ): +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 self._logger.debug(f"Csum set hw error:\n{csum_output}"= ;)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 raise InteractiveCommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 f"Failed to set csum hw {name} mode on port {= port_id}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 success =3D False<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if f"{name} c= hecksum offload is hw" in csum_output.lower():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 succ= ess =3D True
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if not success and= verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self= ._logger.debug(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Failed to set csum hw mode on port {port_id}:\n{csum_outp= ut}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rais= e InteractiveCommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"""Failed to set csum hw mode on port
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{port_id}:\n{= csum_output}"""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) +
+=C2=A0 =C2=A0 def flow_create(self, flow_rule: FlowRule, port_id: int) -&g= t; int:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Creates a flow rule in the t= estpmd session.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 This command is implicitly verified as needed = to return the created flow rule id.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_rule: :class:`FlowRule` obj= ect used for creating testpmd flow rule.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: Integer representing th= e port to use.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If flow rule is invalid.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Id of created flow rule.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_output =3D self.send_command(f"flow = create {port_id} {flow_rule}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 match =3D re.search(r"#(\d+)", flow_= output)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if match is not None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 match_str =3D match.group(1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_id =3D int(match_str)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return flow_id
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug(f"Failed= to create flow rule:\n{flow_output}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError(f"Failed to create flow rule:\n{flow_output}")
+
+=C2=A0 =C2=A0 def flow_validate(self, flow_rule: FlowRule, port_id: int) -= > bool:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Validates a flow rule in the= testpmd session.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_rule: :class:`FlowRule` obj= ect used for validating testpmd flow rule.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: Integer representing th= e port to use.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Boolean representing whether rul= e is valid or not.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_output =3D self.send_command(f"flow = validate {port_id} {flow_rule}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Flow rule validated" in flow_out= put:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return True
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return False
+
+=C2=A0 =C2=A0 def flow_delete(self, flow_id: int, port_id: int, verify: bo= ol =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Deletes the specified flow r= ule from the testpmd session.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_id: ID of the flow to remov= e.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: Integer representing th= e port to use.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 to ensure the flow= rule was deleted successfully.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If flow rule is invalid.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_output =3D self.send_command(f"flow = destroy {port_id} rule {flow_id}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "destroyed" not in = flow_output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to delete flow rule:\n{flow_output}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to delete flow rule:\n{flow_output}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 @_requires_started_ports
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def set_port_mtu(self, port_id: int, mtu: int, verify: bool = =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Change the MTU of a port usi= ng testpmd.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Some PMDs require that the port be stopped bef= ore changing the MTU, and it does no harm to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 stop the port before configuring in cases wher= e it isn't required, so ports are stopped
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 prior to changing their MTU. On the other hand= , some PMDs require that the port had already
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 been started once since testpmd startup. There= fore, ports are also started before stopping
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 them to ensure this has happened.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port to adjus= t the MTU on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mtu: Desired value for the MTU t= o be set to.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If `verify` is :data:`Tr= ue` then the output will be scanned in an attempt to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify that the mt= u was properly set on the port. Defaults to :data:`True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the MTU was not
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 properly updated o= n the port matching `port_id`.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 set_mtu_output =3D self.send_command(f"po= rt config mtu {port_id} {mtu}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify and (f"MTU: {mtu}" not in = self.send_command(f"show port info {port_id}")):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"Failed to s= et mtu to {mtu} on port {port_id}. Output was:\n{set_mtu_output}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"Test pmd fa= iled to update mtu of port {port_id} to {mtu}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def set_port_mtu_all(self, mtu: int, verify: bool =3D True) = -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Change the MTU of all ports = using testpmd.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Runs :meth:`set_port_mtu` for every port that = testpmd is aware of.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mtu: Desired value for the MTU t= o be set to.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: Whether to verify that s= etting the MTU on each port was successful or not.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Defaults to :data:= `True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the MTU was not
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 properly updated o= n at least one port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for port in self.ports:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.set_port_mtu(port.id, mtu, verify)=
+
+=C2=A0 =C2=A0 @staticmethod
+=C2=A0 =C2=A0 def extract_verbose_output(output: str) -> list[TestPmdVe= rbosePacket]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Extract the verbose informat= ion present in given testpmd output.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 This method extracts sections of verbose outpu= t that begin with the line
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 "port X/queue Y: sent/received Z packets&= quot; and end with the ol_flags of a packet.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 output: Testpmd output that cont= ains verbose information
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 List of parsed packet informatio= n gathered from verbose information in `output`.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 out: list[TestPmdVerbosePacket] =3D []
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 prev_header: str =3D ""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 iter =3D re.finditer(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"(?P<HEADER>(?:port = \d+/queue \d+: (?:received|sent) \d+ packets)?)\s*"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"(?P<PACKET>src=3D[\= w\s=3D:-]+?ol_flags: [\w ]+)",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 output,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for match in iter:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if match.group("HEADER"= ;):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 prev_header =3D ma= tch.group("HEADER")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 out.append(TestPmdVerbosePacket.= parse(f"{prev_header}\n{match.group('PACKET')}"))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return out
+
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def set_vlan_filter(self, port: int, enable: bool, verify: b= ool =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set vlan filter on.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port number to enable = VLAN filter on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: Enable the filter on `po= rt` if :data:`True`, otherwise disable it.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command and show port info
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is scanned to veri= fy that vlan filtering was set successfully.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the filter
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to update. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 filter_cmd_output =3D self.send_command(f"= ;vlan set filter {'on' if enable else 'off'} {port}")<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan_settings =3D self.show_port= _info(port_id=3Dport).vlan_offload
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if enable ^ (vlan_settings is no= t None and VLANOffloadFlag.FILTER in vlan_settings):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;""Failed to {"enable" if enable else "disable&q= uot;}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0filter on port {port}: = \n{filter_cmd_output}"""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;""Failed to {"enable" if enable else "disable&q= uot;}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 filt= er on port {port}"""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def set_mac_address(self, port: int, mac_address: str, verif= y: bool =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set port's MAC address.<= br> +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The number of the requeste= d port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mac_address: The MAC address to = set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned to verify that
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the mac address is= set in the specified port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the command
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to execute.<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"mac_addr s= et {port} {mac_address}", skip_first_line=3DTrue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if output.strip():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set MAC address {mac_address} on port {port}." +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set MAC address {mac_address} on port {port}." +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def set_flow_control(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port: int, flow_ctrl: TestPmdPortFlowCtr= l, verify: bool =3D True
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set the given `port`'s f= low control.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The number of the requeste= d port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_ctrl: The requested flow co= ntrol parameters.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned to verify that
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the flow control i= n the specified port is set.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the command
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to execute.<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"set flow_c= trl {flow_ctrl} {port}", skip_first_line=3DTrue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if output.strip():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Testpmd failed to set the {flow_ctrl} in port {port}.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set the {flow_ctrl} in port {port}."
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def show_port_flow_info(self, port: int) -> TestPmdPortFl= owCtrl | None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Show port info flow.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The number of the requeste= d port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The current port flow control pa= rameters if supported, otherwise :data:`None`.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"show port = {port} flow_ctrl")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Flow control infos" in output: +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return TestPmdPortFlowCtrl.parse= (output)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return None
+
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def rx_vlan(self, vlan: int, port: int, add: bool, verify: b= ool =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add specified vlan tag to th= e filter list on a port. Requires vlan filter to be on.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan: The vlan tag to add, shoul= d be within 1-1005.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port number to add the= tag on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add: Adds the tag if :data:`True= `, otherwise removes the tag.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned to verify that
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the vlan tag was a= dded to the filter list on the specified port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the tag
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is not added.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 rx_cmd_output =3D self.send_command(f"rx_= vlan {'add' if add else 'rm'} {vlan} {port}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "VLAN-filteri= ng disabled" in rx_cmd_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or "Invalid v= lan_id" in rx_cmd_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or "Bad argum= ents" in rx_cmd_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;""Failed to {"add" if add else "remove"} t= ag {vlan}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port= {port}: \n{rx_cmd_output}"""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to {'add' if add else 'remove'} tag {vlan= } on port {port}."
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def set_vlan_strip(self, port: int, enable: bool, verify: bo= ol =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Enable or disable vlan strip= ping on the specified port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port number to use. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: If :data:`True`, will tu= rn vlan stripping on, otherwise will turn off.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command and show port info
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is scanned to veri= fy that vlan stripping was enabled on the specified port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and stripping
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to update. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 strip_cmd_output =3D self.send_command(f"= vlan set strip {'on' if enable else 'off'} {port}") +=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan_settings =3D self.show_port= _info(port_id=3Dport).vlan_offload
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if enable ^ (vlan_settings is no= t None and VLANOffloadFlag.STRIP in vlan_settings):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;""Failed to set strip {"on" if enable else "off= "}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port= {port}: \n{strip_cmd_output}"""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set strip {'on' if enable else 'off'} = port {port}."
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def tx_vlan_set(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port: int, enable: bool, vlan: int | Non= e =3D None, verify: bool =3D True
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set hardware insertion of vl= an tags in packets sent on a port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port number to use. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: Sets vlan tag insertion = if :data:`True`, and resets if :data:`False`.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan: The vlan tag to insert if = enable is :data:`True`.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned to verify that
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan insertion was= enabled on the specified port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the insertion
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 tag is not set. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if enable:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 tx_vlan_cmd_output =3D self.send= _command(f"tx_vlan set {port} {vlan}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &quo= t;Please stop port" in tx_vlan_cmd_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or &= quot;Invalid vlan_id" in tx_vlan_cmd_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or &= quot;Invalid port" in tx_vlan_cmd_output
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self= ._logger.debug(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Failed to set vlan tag {vlan} on port {port}:\n{tx_vlan_c= md_output}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rais= e InteractiveCommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Testpmd failed to set vlan insertion tag {vlan} on port {= port}."
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) +=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 tx_vlan_cmd_output =3D self.send= _command(f"tx_vlan reset {port}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Please st= op port" in tx_vlan_cmd_output or "Invalid port" in tx_vlan_= cmd_output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self= ._logger.debug(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Failed to reset vlan insertion on port {port}: \n{tx_vlan= _cmd_output}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rais= e InteractiveCommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Testpmd failed to reset vlan insertion on port {port}.&qu= ot;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) +
+=C2=A0 =C2=A0 def set_promisc(self, port: int, enable: bool, verify: bool = =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Enable or disable promiscuou= s mode for the specified port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: Port number to use.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: If :data:`True`, turn pr= omiscuous mode on, otherwise turn off.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` an addit= ional command will be sent to verify that
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 promiscuous mode i= s properly set. Defaults to :data:`True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and promiscuous mode
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is not correctly s= et.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 promisc_cmd_output =3D self.send_command(f&quo= t;set promisc {port} {'on' if enable else 'off'}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stats =3D self.show_port_info(po= rt_id=3Dport)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if enable ^ stats.is_promiscuous= _mode_enabled:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to set promiscuous mode on port {port}: \n{promisc_cmd_output}&qu= ot;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set promiscuous mode on port {port}."
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def set_verbose(self, level: int, verify: bool =3D True) -&g= t; None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set debug verbosity level. +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 level: 0 - silent except for err= or
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 1 - fully verbose = except for Tx packets
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 2 - fully verbose = except for Rx packets
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >2 - fully verb= ose
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the comm= and output will be scanned to verify that verbose level
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is properly set. D= efaults to :data:`True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and verbose level
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is not correctly set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 verbose_cmd_output =3D self.send_command(f&quo= t;set verbose {level}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Change verbose level&qu= ot; not in verbose_cmd_output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to set verbose level to {level}: \n{verbose_cmd_output}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set verbose level to {level}."
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def rx_vxlan(self, vxlan_id: int, port_id: int, enable: bool= , verify: bool =3D True) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add or remove vxlan id to/fr= om filter list.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vxlan_id: VXLAN ID to add to por= t filter list.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port to modif= y VXLAN filter of.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: If :data:`True`, adds sp= ecified VXLAN ID, otherwise removes it.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is checked to verify
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the VXLAN ID was s= uccessfully added/removed from the port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and VXLAN ID
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is not successfull= y added or removed.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 action =3D "add" if enable else &quo= t;rm"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vxlan_output =3D self.send_command(f"rx_v= xlan_port {action} {vxlan_id} {port_id}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "udp tunneling add error= " in vxlan_output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to set VXLAN:\n{vxlan_output}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(f"Failed to set VXLAN:\n{vxlan_output}")
+
+=C2=A0 =C2=A0 def clear_port_stats(self, port_id: int, verify: bool =3D Tr= ue) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Clear statistics of a given = port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port to clear= the statistics on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the command will be scanned to verify that it was
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 successful, otherw= ise failures will be ignored. Defaults to :data:`True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and testpmd fails to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clear the statisti= cs of the given port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 clear_output =3D self.send_command(f"clea= r port stats {port_id}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify and f"NIC statistics for port {= port_id} cleared" not in clear_output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"Test pmd fa= iled to set clear forwarding stats on port {port_id}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def clear_port_stats_all(self, verify: bool =3D True) -> = None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Clear the statistics of all = ports that testpmd is aware of.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the command will be scanned to verify that all
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ports had their st= atistics cleared, otherwise failures will be ignored. Defaults to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 :data:`True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and testpmd fails to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clear the statisti= cs of any of its ports.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 clear_output =3D self.send_command("clear= port stats all")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if type(self._app_params.port_nu= ma_config) is list:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for port_id in ran= ge(len(self._app_params.port_numa_config)):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if f= "NIC statistics for port {port_id} cleared" not in clear_output:<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 raise InteractiveCommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 f"Test pmd failed to set clear forwarding sta= ts on port {port_id}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 )
+
+=C2=A0 =C2=A0 @only_active
+=C2=A0 =C2=A0 def close(self) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Overrides :meth:`~.interacti= ve_shell.close`."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.stop()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command("quit", "Bye.= ..")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return super().close()
+
+=C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =3D=3D=3D=3D=3D=3D Capability retrieval methods =3D=3D=3D=3D= =3D=3D
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 def get_capabilities_rx_offload(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get all rx offload capabilit= ies and divide them into supported and unsupported.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Getting rx offload ca= pabilities.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show port {self.ports[0].id= } rx_offload capabilities"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 rx_offload_capabilities_out =3D self.send_comm= and(command)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 rx_offload_capabilities =3D RxOffloadCapabilit= ies.parse(rx_offload_capabilities_out)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._update_capabilities_from_flag(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RxOffloadCapability,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rx_offload_capabilities.per_port= | rx_offload_capabilities.per_queue,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def get_port_queue_info(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, queue_id: int, is_rx_queue= : bool
+=C2=A0 =C2=A0 ) -> TestPmdQueueInfo:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the current state of= the specified queue."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show {'rxq' if is_r= x_queue else 'txq'} info {port_id} {queue_id}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_info =3D TestPmdQueueInfo.parse(self.sen= d_command(command))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return queue_info
+
+=C2=A0 =C2=A0 def setup_port_queue(self, port_id: int, queue_id: int, is_r= x_queue: bool) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Setup a given queue on a por= t.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 This functionality cannot be verified because = the setup action only takes effect when the
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue is started.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port where th= e queue resides.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: ID of the queue to set= up.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Type of queue to se= tup. If :data:`True` an RX queue will be setup,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 otherwise a TX que= ue will be setup.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port {port_id} {'= rxq' if is_rx_queue else 'txq'} {queue_id} setup")
+
+=C2=A0 =C2=A0 def stop_port_queue(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, queue_id: int, is_rx_queue= : bool, verify: bool =3D True
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Stops a given queue on a por= t.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port that the= queue belongs to.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: ID of the queue to sto= p.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Type of queue to st= op. If :data:`True` an RX queue will be stopped,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 otherwise a TX que= ue will be stopped.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` an addit= ional command will be sent to verify the queue stopped.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Defaults to :data:= `True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the queue fails to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stop.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_type =3D "rxq" if is_rx_queue e= lse "txq"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 stop_cmd_output =3D self.send_command(f"p= ort {port_id} {port_type} {queue_id} stop")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_started =3D self.get_port_= queue_info(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id, queue_id,= is_rx_queue
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ).is_queue_started
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if queue_started:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_outp= ut}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Test pmd failed to stop {port_type} {queue_id} on port {port_id}" +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def start_port_queue(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, queue_id: int, is_rx_queue= : bool, verify: bool =3D True
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Starts a given queue on a po= rt.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 First sets up the port queue, then starts it.<= br> +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port that the= queue belongs to.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: ID of the queue to sta= rt.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Type of queue to st= art. If :data:`True` an RX queue will be started,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 otherwise a TX que= ue will be started.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: if :data:`True` an addit= ional command will be sent to verify that the queue was
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 started. Defaults = to :data:`True`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the queue fails to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 start.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_type =3D "rxq" if is_rx_queue e= lse "txq"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.setup_port_queue(port_id, queue_id, is_rx= _queue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 start_cmd_output =3D self.send_command(f"= port {port_id} {port_type} {queue_id} start")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_started =3D self.get_port_= queue_info(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id, queue_id,= is_rx_queue
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ).is_queue_started
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if not queue_started:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_ou= tput}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Test pmd failed to start {port_type} {queue_id} on port {port_id}"<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def get_queue_ring_size(self, port_id: int, queue_id: int, i= s_rx_queue: bool) -> int:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the current size of = the ring on the specified queue."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show {'rxq' if is_r= x_queue else 'txq'} info {port_id} {queue_id}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_info =3D TestPmdQueueInfo.parse(self.sen= d_command(command))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return queue_info.ring_size
+
+=C2=A0 =C2=A0 def set_queue_ring_size(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: int,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: int,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 size: int,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: bool,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: bool =3D True,
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Update the ring size of an R= x/Tx queue on a given port.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Queue is setup after setting the ring size so = that the queue info reflects this change and
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 it can be verified.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port that the queue= resides on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: The ID of the queue on= the port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size: The size to update the rin= g size to.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Whether to modify a= n RX or TX queue. If :data:`True` an RX queue will be
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 updated, otherwise= a TX queue will be updated.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` an addit= ional command will be sent to check the ring size of
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the queue in an at= tempt to validate that the size was changes properly.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and there is a failure
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 when updating ring= size.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_type =3D "rxq" if is_rx_queue = else "txq"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port config {port_id}= {queue_type} {queue_id} ring_size {size}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.setup_port_queue(port_id, queue_id, is_rx= _queue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 curr_ring_size =3D self.get_queu= e_ring_size(port_id, queue_id, is_rx_queue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if curr_ring_size !=3D size:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed up update ring size of queue {queue_id} on port {port_id}. Curren= t"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot; ring size is {curr_ring_size}."
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to update ring size of queue {queue_id} on port {port_id}" +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 @_requires_stopped_ports
+=C2=A0 =C2=A0 def set_queue_deferred_start(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, queue_id: int, is_rx_queue= : bool, on: bool
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set the deferred start attri= bute of the specified queue on/off.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port that the queue= resides on.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: The ID of the queue on= the port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Whether to modify a= n RX or TX queue. If :data:`True` an RX queue will be
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 updated, otherwise= a TX queue will be updated.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 on: Whether to set deferred star= t mode on or off. If :data:`True` deferred start will
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 be turned on, othe= rwise it will be turned off.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_type =3D "rxq" if is_rx_queue = else "txq"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 action =3D "on" if on else "off= "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port {port_id} {queue= _type} {queue_id} deferred_start {action}")
+
+=C2=A0 =C2=A0 def _update_capabilities_from_flag(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag_class: type[Flag],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_flags: Flag,
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Divide all flags from `flag_= class` into supported and unsupported."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for flag in flag_class:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if flag in supported_flags:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabili= ties.add(NicCapability[str(flag.name)])
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabi= lities.add(NicCapability[str(flag.name)])
+
+=C2=A0 =C2=A0 @_requires_started_ports
+=C2=A0 =C2=A0 def get_capabilities_rxq_info(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get all rxq capabilities and= divide them into supported and unsupported.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Getting rxq capabilit= ies.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show rxq info {self.ports[0= ].id} 0"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 rxq_info =3D TestPmdRxqInfo.parse(self.send_co= mmand(command))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if rxq_info.scattered_packets:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities.add(NicCa= pability.SCATTERED_RX_ENABLED)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities.add(Nic= Capability.SCATTERED_RX_ENABLED)
+
+=C2=A0 =C2=A0 def get_capabilities_show_port_info(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get all capabilities from sh= ow port info and divide them into supported and unsupported.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._update_capabilities_from_flag(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 DeviceCapabilitiesFlag,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.ports[0].device_capabilitie= s,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 def get_capabilities_mcast_filtering(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get multicast filtering capa= bility from mcast_addr add and check for testpmd error code.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Getting mcast filter = capabilities.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"mcast_addr add {self.ports[= 0].id} 01:00:5E:00:00:00"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(command)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "diag=3D-95" in output:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities.add(Nic= Capability.MCAST_FILTERING)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities.add(NicCa= pability.MCAST_FILTERING)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D str.replace(command,= "add", "remove", 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(command)
+
+=C2=A0 =C2=A0 def get_capabilities_flow_ctrl(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get flow control capability = and check for testpmd failure.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Getting flow ctrl cap= abilities.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show port {self.ports[0].id= } flow_ctrl"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(command)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Flow control infos" in output: +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities.add(NicCa= pability.FLOW_CTRL)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities.add(Nic= Capability.FLOW_CTRL)
+
+=C2=A0 =C2=A0 def get_capabilities_physical_function(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
+=C2=A0 =C2=A0 ) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Store capability representin= g a physical function test run.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ctx =3D get_ctx()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if ctx.topology.vf_ports =3D=3D []:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities.add(NicCa= pability.PHYSICAL_FUNCTION)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities.add(Nic= Capability.PHYSICAL_FUNCTION)
diff --git a/dts/framework/params/testpmd.py b/dts/api/testpmd/config.py similarity index 98%
rename from dts/framework/params/testpmd.py
rename to dts/api/testpmd/config.py
index 1913bd0fa2..e71a3e1ef0 100644
--- a/dts/framework/params/testpmd.py
+++ b/dts/api/testpmd/config.py
@@ -1,7 +1,12 @@
=C2=A0# SPDX-License-Identifier: BSD-3-Clause
=C2=A0# Copyright(c) 2024 Arm Limited

-"""Module containing all the TestPmd-related parameter clas= ses."""
+"""Module containing all classes needed to configure :class= :`TestPmd`.
+
+This module defines the :class:`TestPmdParams` class which is used to conf= igure the
+TestPmd shell. It also includes various data classes and enums that are us= ed
+to represent different configurations and settings.
+"""

=C2=A0from dataclasses import dataclass, field
=C2=A0from enum import EnumMeta, Flag, auto, unique
@@ -146,7 +151,7 @@ class RSSSetting(EnumMeta):


=C2=A0class SimpleForwardingModes(StrEnum):
-=C2=A0 =C2=A0 r"""The supported packet forwarding modes for= :class:`~TestPmdShell`\s."""
+=C2=A0 =C2=A0 r"""The supported packet forwarding modes for= :class:`~TestPmd`\s."""

=C2=A0 =C2=A0 =C2=A0#:
=C2=A0 =C2=A0 =C2=A0io =3D auto()
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
new file mode 100644
index 0000000000..553438c904
--- /dev/null
+++ b/dts/api/testpmd/types.py
@@ -0,0 +1,1406 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 University of New Hampshire
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2024 Arm Limited
+
+"""TestPmd types module.
+
+Exposes types used in the TestPmd API.
+"""
+
+import re
+from dataclasses import dataclass, field
+from enum import Flag, auto
+from typing import Literal
+
+from typing_extensions import Self
+
+from framework.parser import ParserFn, TextParser
+from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
+
+
+class TestPmdDevice:
+=C2=A0 =C2=A0 """The data of a device that testpmd can reco= gnize.
+
+=C2=A0 =C2=A0 Attributes:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 pci_address: The PCI address of the device. +=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 pci_address: str
+
+=C2=A0 =C2=A0 def __init__(self, pci_address_line: str):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Initialize the device from t= he testpmd output line string.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pci_address_line: A line of test= pmd output that contains a device.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.pci_address =3D pci_address_line.strip().= split(": ")[1].strip()
+
+=C2=A0 =C2=A0 def __str__(self) -> str:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """The PCI address captures wha= t the device is."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self.pci_address
+
+
+class VLANOffloadFlag(Flag):
+=C2=A0 =C2=A0 """Flag representing the VLAN offload setting= s of a NIC port."""
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 STRIP =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 FILTER =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 EXTEND =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 QINQ_STRIP =3D auto()
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def from_str_dict(cls, d):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes an instance from a dic= t containing the flag member names with an "on" value.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 d: A dictionary containing the f= lag members as keys and any string value.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 A new instance of the flag.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name in cls.__members__:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if d.get(name) =3D=3D "on&q= uot;:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls[name= ]
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"VLAN offloa= d:\s+"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"strip (?P&l= t;STRIP>on|off), "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"filter (?P&= lt;FILTER>on|off), "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"extend (?P&= lt;EXTEND>on|off), "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"qinq strip = (?P<QINQ_STRIP>on|off)",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 re.MULTILINE,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 named=3DTrue,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls.from_str_dict,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+
+class ChecksumOffloadOptions(Flag):
+=C2=A0 =C2=A0 """Flag representing checksum hardware offloa= d layer options."""
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ip =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 udp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 tcp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 sctp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 outer_ip =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 outer_udp =3D auto()
+
+
+class RSSOffloadTypesFlag(Flag):
+=C2=A0 =C2=A0 """Flag representing the RSS offload flow typ= es supported by the NIC port."""
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv4 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv4_frag =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv4_tcp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv4_udp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv4_sctp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv4_other =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_frag =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_tcp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_udp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_sctp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_other =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l2_payload =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_ex =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_tcp_ex =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_udp_ex =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 port =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 vxlan =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 geneve =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 nvgre =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_22 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 gtpu =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 eth =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 s_vlan =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 c_vlan =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 esp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ah =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l2tpv3 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 pfcp =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 pppoe =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ecpri =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 mpls =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv4_chksum =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l4_chksum =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l2tpv2 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ipv6_flow_label =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_38 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_39 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_40 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_41 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_42 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_43 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_44 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_45 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_46 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_47 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_48 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_49 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_50 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_defined_51 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_pre96 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_pre64 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_pre56 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_pre48 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_pre40 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_pre32 =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l2_dst_only =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l2_src_only =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l4_dst_only =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l4_src_only =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_dst_only =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_src_only =3D auto()
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ip =3D ipv4 | ipv4_frag | ipv4_other | ipv6 | ipv6_frag | ip= v6_other | ipv6_ex
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 udp =3D ipv4_udp | ipv6_udp | ipv6_udp_ex
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 tcp =3D ipv4_tcp | ipv6_tcp | ipv6_tcp_ex
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 sctp =3D ipv4_sctp | ipv6_sctp
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 tunnel =3D vxlan | geneve | nvgre
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 vlan =3D s_vlan | c_vlan
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 all =3D (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 eth
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | vlan
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | ip
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | tcp
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | udp
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | sctp
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | l2_payload
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | l2tpv3
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | esp
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | ah
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | pfcp
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | gtpu
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | ecpri
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | mpls
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | l2tpv2
+=C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def from_list_string(cls, names: str) -> Self:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag from a whitespa= ce-separated list of names.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 names: a whitespace-separated li= st containing the members of this flag.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name in names.split():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls.from_str(name)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def from_str(cls, name: str) -> Self:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag matching the su= pplied name.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name: a valid member of this fla= g in text
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 member_name =3D name.strip().replace("-&q= uot;, "_")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return cls[member_name]
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(r"Supported= RSS offload flow types:((?:\r?\n?=C2=A0 \S+)+)", re.MULTILINE),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RSSOffloadTypesFlag.from_list_st= ring,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+
+class DeviceCapabilitiesFlag(Flag):
+=C2=A0 =C2=A0 """Flag representing the device capabilities.= """
+
+=C2=A0 =C2=A0 #: Device supports Rx queue setup after device started.
+=C2=A0 =C2=A0 RUNTIME_RX_QUEUE_SETUP =3D auto()
+=C2=A0 =C2=A0 #: Device supports Tx queue setup after device started.
+=C2=A0 =C2=A0 RUNTIME_TX_QUEUE_SETUP =3D auto()
+=C2=A0 =C2=A0 #: Device supports shared Rx queue among ports within Rx dom= ain and switch domain.
+=C2=A0 =C2=A0 RXQ_SHARE =3D auto()
+=C2=A0 =C2=A0 #: Device supports keeping flow rules across restart.
+=C2=A0 =C2=A0 FLOW_RULE_KEEP =3D auto()
+=C2=A0 =C2=A0 #: Device supports keeping shared flow objects across restar= t.
+=C2=A0 =C2=A0 FLOW_SHARED_OBJECT_KEEP =3D auto()
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find_int(r"Devic= e capabilities: (0x[A-Fa-f\d]+)"),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+
+class DeviceErrorHandlingMode(StrEnum):
+=C2=A0 =C2=A0 """Enum representing the device error handlin= g mode."""
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 none =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 passive =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 proactive =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 unknown =3D auto()
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this enum from text.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(TextParser.find(r"= Device error handling mode: (\w+)"), cls)
+
+
+class RxQueueState(StrEnum):
+=C2=A0 =C2=A0 """RX queue states.
+
+=C2=A0 =C2=A0 References:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK lib: ``lib/ethdev/rte_ethdev.h``
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/confi= g.c:get_queue_state_name()``
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 stopped =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 started =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 hairpin =3D auto()
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 unknown =3D auto()
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this enum from text.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(TextParser.find(r"= Rx queue state: ([^\r\n]+)"), cls)
+
+
+@dataclass
+class TestPmdQueueInfo(TextParser):
+=C2=A0 =C2=A0 """Dataclass representation of the common par= ts of the testpmd `show rxq/txq info` commands."""
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 prefetch_threshold: int =3D field(metadata=3DTextParser.find= _int(r"prefetch threshold: (\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 host_threshold: int =3D field(metadata=3DTextParser.find_int= (r"host threshold: (\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 writeback_threshold: int =3D field(metadata=3DTextParser.fin= d_int(r"writeback threshold: (\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 free_threshold: int =3D field(metadata=3DTextParser.find_int= (r"free threshold: (\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 deferred_start: bool =3D field(metadata=3DTextParser.find(&q= uot;deferred start: on"))
+=C2=A0 =C2=A0 #: The number of RXD/TXDs is just the ring size of the queue= .
+=C2=A0 =C2=A0 ring_size: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;Number of (?:RXDs|TXDs): (\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 is_queue_started: bool =3D field(metadata=3DTextParser.find(= "queue state: started"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 burst_mode: str | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;Burst mode: ([^\r\n]+)")
+=C2=A0 =C2=A0 )
+
+
+@dataclass
+class TestPmdTxqInfo(TestPmdQueueInfo):
+=C2=A0 =C2=A0 """Representation of testpmd's ``show txq= info <port_id> <queue_id>`` command.
+
+=C2=A0 =C2=A0 References:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd command function: ``app/test-pmd/cmdli= ne.c:cmd_showqueue()``
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/confi= g.c:rx_queue_infos_display()``
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #: Ring size threshold
+=C2=A0 =C2=A0 rs_threshold: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"TX RS threshold: (\d+)\b")
+=C2=A0 =C2=A0 )
+
+
+@dataclass
+class TestPmdRxqInfo(TestPmdQueueInfo):
+=C2=A0 =C2=A0 """Representation of testpmd's ``show rxq= info <port_id> <queue_id>`` command.
+
+=C2=A0 =C2=A0 References:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd command function: ``app/test-pmd/cmdli= ne.c:cmd_showqueue()``
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/confi= g.c:rx_queue_infos_display()``
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #: Mempool used by that queue
+=C2=A0 =C2=A0 mempool: str | None =3D field(default=3DNone, metadata=3DTex= tParser.find(r"Mempool: ([^\r\n]+)"))
+=C2=A0 =C2=A0 #: Drop packets if no descriptors are available
+=C2=A0 =C2=A0 drop_packets: bool | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;RX drop packets: on")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Scattered packets Rx enabled
+=C2=A0 =C2=A0 scattered_packets: bool | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;RX scattered packets: on")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: The state of the queue
+=C2=A0 =C2=A0 queue_state: str | None =3D field(default=3DNone, metadata= =3DRxQueueState.make_parser())
+
+
+@dataclass
+class TestPmdPort(TextParser):
+=C2=A0 =C2=A0 """Dataclass representing the result of testp= md's ``show port info`` command."""
+
+=C2=A0 =C2=A0 @staticmethod
+=C2=A0 =C2=A0 def _make_device_private_info_parser() -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Device private information p= arser.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Ensures that we are not parsing invalid device= private info output.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a parser
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 function that pars= es the device private info from the TestPmd port info output.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 def _validate(info: str):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 info =3D info.strip()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 info =3D=3D "= none"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or info.startswith= ("Invalid file")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or info.startswith= ("Failed to dump")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return None
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return info
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(TextParser.find(r"= Device private info:\s+([\s\S]+)"), _validate)
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 id: int =3D field(metadata=3DTextParser.find_int(r"Info= s for port (\d+)\b"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 device_name: str =3D field(metadata=3DTextParser.find(r"= ;Device name: ([^\r\n]+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 driver_name: str =3D field(metadata=3DTextParser.find(r"= ;Driver name: ([^\r\n]+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 socket_id: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;Connect to socket: (\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 is_link_up: bool =3D field(metadata=3DTextParser.find("= Link status: up"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 link_speed: str =3D field(metadata=3DTextParser.find(r"= Link speed: ([^\r\n]+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 is_link_full_duplex: bool =3D field(metadata=3DTextParser.fi= nd("Link duplex: full-duplex"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 is_link_autonegotiated: bool =3D field(metadata=3DTextParser= .find("Autoneg status: On"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 is_promiscuous_mode_enabled: bool =3D field(metadata=3DTextP= arser.find("Promiscuous mode: enabled"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 is_allmulticast_mode_enabled: bool =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find("Allmulticast = mode: enabled")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Maximum number of MAC addresses
+=C2=A0 =C2=A0 max_mac_addresses_num: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Maximum = number of MAC addresses: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Maximum configurable length of RX packet
+=C2=A0 =C2=A0 max_hash_mac_addresses_num: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Maximum = number of MAC addresses of hash filtering: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Minimum size of RX buffer
+=C2=A0 =C2=A0 min_rx_bufsize: int =3D field(metadata=3DTextParser.find_int= (r"Minimum size of RX buffer: (\d+)"))
+=C2=A0 =C2=A0 #: Maximum configurable length of RX packet
+=C2=A0 =C2=A0 max_rx_packet_length: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Maximum = configurable length of RX packet: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Maximum configurable size of LRO aggregated packet
+=C2=A0 =C2=A0 max_lro_packet_size: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Maximum = configurable size of LRO aggregated packet: (\d+)")
+=C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 #: Current number of RX queues
+=C2=A0 =C2=A0 rx_queues_num: int =3D field(metadata=3DTextParser.find_int(= r"Current number of RX queues: (\d+)"))
+=C2=A0 =C2=A0 #: Max possible RX queues
+=C2=A0 =C2=A0 max_rx_queues_num: int =3D field(metadata=3DTextParser.find_= int(r"Max possible RX queues: (\d+)"))
+=C2=A0 =C2=A0 #: Max possible number of RXDs per queue
+=C2=A0 =C2=A0 max_queue_rxd_num: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Max poss= ible number of RXDs per queue: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Min possible number of RXDs per queue
+=C2=A0 =C2=A0 min_queue_rxd_num: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Min poss= ible number of RXDs per queue: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: RXDs number alignment
+=C2=A0 =C2=A0 rxd_alignment_num: int =3D field(metadata=3DTextParser.find_= int(r"RXDs number alignment: (\d+)"))
+
+=C2=A0 =C2=A0 #: Current number of TX queues
+=C2=A0 =C2=A0 tx_queues_num: int =3D field(metadata=3DTextParser.find_int(= r"Current number of TX queues: (\d+)"))
+=C2=A0 =C2=A0 #: Max possible TX queues
+=C2=A0 =C2=A0 max_tx_queues_num: int =3D field(metadata=3DTextParser.find_= int(r"Max possible TX queues: (\d+)"))
+=C2=A0 =C2=A0 #: Max possible number of TXDs per queue
+=C2=A0 =C2=A0 max_queue_txd_num: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Max poss= ible number of TXDs per queue: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Min possible number of TXDs per queue
+=C2=A0 =C2=A0 min_queue_txd_num: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Min poss= ible number of TXDs per queue: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: TXDs number alignment
+=C2=A0 =C2=A0 txd_alignment_num: int =3D field(metadata=3DTextParser.find_= int(r"TXDs number alignment: (\d+)"))
+=C2=A0 =C2=A0 #: Max segment number per packet
+=C2=A0 =C2=A0 max_packet_segment_num: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Max segm= ent number per packet: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Max segment number per MTU/TSO
+=C2=A0 =C2=A0 max_mtu_segment_num: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Max segm= ent number per MTU\/TSO: (\d+)")
+=C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 device_capabilities: DeviceCapabilitiesFlag =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DDeviceCapabilitiesFlag.make_parser(= ),
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 device_error_handling_mode: DeviceErrorHandlingMode | None = =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DDeviceErrorHandling= Mode.make_parser()
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 device_private_info: str | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3D_make_device_private_info_parser(),=
+=C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 hash_key_size: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Hash key size in bytes: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 redirection_table_size: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Redirection table size: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 supported_rss_offload_flow_types: RSSOffloadTypesFlag =3D fi= eld(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DRSSOffloadTypesFlag(0), metadata=3DR= SSOffloadTypesFlag.make_parser()
+=C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 mac_address: str | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;MAC address: ([A-Fa-f0-9:]+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 fw_version: str | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;Firmware-version: ([^\r\n]+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 dev_args: str | None =3D field(default=3DNone, metadata=3DTe= xtParser.find(r"Devargs: ([^\r\n]+)"))
+=C2=A0 =C2=A0 #: Socket id of the memory allocation
+=C2=A0 =C2=A0 mem_alloc_socket_id: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"memory a= llocation on the socket: (\d+)"),
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 mtu: int | None =3D field(default=3DNone, metadata=3DTextPar= ser.find_int(r"MTU: (\d+)"))
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 vlan_offload: VLANOffloadFlag | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DVLANOffloadFlag.make_parser(),
+=C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 #: Maximum size of RX buffer
+=C2=A0 =C2=A0 max_rx_bufsize: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Maximum size of RX buffer: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Maximum number of VFs
+=C2=A0 =C2=A0 max_vfs_num: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Maximum number of VFs: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Maximum number of VMDq pools
+=C2=A0 =C2=A0 max_vmdq_pools_num: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Maximum number of VMDq pools: (\d+)")
+=C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 switch_name: str | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;Switch name: ([\r\n]+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 switch_domain_id: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Switch domain Id: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 switch_port_id: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Switch Port Id: (\d+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 switch_rx_domain: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Switch Rx domain: (\d+)")
+=C2=A0 =C2=A0 )
+
+
+@dataclass
+class TestPmdPortStats(TextParser):
+=C2=A0 =C2=A0 """Port statistics."""
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 port_id: int =3D field(metadata=3DTextParser.find_int(r"= ;NIC statistics for port (\d+)"))
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 rx_packets: int =3D field(metadata=3DTextParser.find_int(r&q= uot;RX-packets:\s+(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 rx_missed: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;RX-missed:\s+(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 rx_bytes: int =3D field(metadata=3DTextParser.find_int(r&quo= t;RX-bytes:\s+(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 rx_errors: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;RX-errors:\s+(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 rx_nombuf: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;RX-nombuf:\s+(\d+)"))
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 tx_packets: int =3D field(metadata=3DTextParser.find_int(r&q= uot;TX-packets:\s+(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 tx_errors: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;TX-errors:\s+(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 tx_bytes: int =3D field(metadata=3DTextParser.find_int(r&quo= t;TX-bytes:\s+(\d+)"))
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 rx_pps: int =3D field(metadata=3DTextParser.find_int(r"= Rx-pps:\s+(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 rx_bps: int =3D field(metadata=3DTextParser.find_int(r"= Rx-bps:\s+(\d+)"))
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 tx_pps: int =3D field(metadata=3DTextParser.find_int(r"= Tx-pps:\s+(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 tx_bps: int =3D field(metadata=3DTextParser.find_int(r"= Tx-bps:\s+(\d+)"))
+
+
+@dataclass(kw_only=3DTrue)
+class FlowRule:
+=C2=A0 =C2=A0 """Class representation of flow rule paramete= rs.
+
+=C2=A0 =C2=A0 This class represents the parameters of any flow rule as per= the
+=C2=A0 =C2=A0 following pattern:
+
+=C2=A0 =C2=A0 [group {group_id}] [priority {level}] [ingress] [egress]
+=C2=A0 =C2=A0 [user_id {user_id}] pattern {item} [/ {item} [...]] / end +=C2=A0 =C2=A0 actions {action} [/ {action} [...]] / end
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 group_id: int | None =3D None
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 priority_level: int | None =3D None
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 direction: Literal["ingress", "egress"]<= br> +=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 user_id: int | None =3D None
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 pattern: list[str]
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 actions: list[str]
+
+=C2=A0 =C2=A0 def __str__(self) -> str:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the string represent= ation of this instance."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D ""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 pattern =3D " / ".join(self.pattern)=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 action =3D " / ".join(self.actions)<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.group_id is not None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"group {self.grou= p_id} "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.priority_level is not None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"priority {self.p= riority_level} "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"{self.direction} "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.user_id is not None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"user_id {self.us= er_id} "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"pattern {pattern} / end "=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"actions {action} / end" +=C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret
+
+
+class PacketOffloadFlag(Flag):
+=C2=A0 =C2=A0 """Flag representing the Packet Offload Featu= res Flags in DPDK.
+
+=C2=A0 =C2=A0 Values in this class are taken from the definitions in the R= TE MBUF core library in DPDK
+=C2=A0 =C2=A0 located in ``lib/mbuf/rte_mbuf_core.h``. It is expected that= flag values in this class will
+=C2=A0 =C2=A0 match the values they are set to in said DPDK library with o= ne exception; all values must be
+=C2=A0 =C2=A0 unique. For example, the definitions for unknown checksum fl= ags in ``rte_mbuf_core.h`` are all
+=C2=A0 =C2=A0 set to :data:`0`, but it is valuable to distinguish between = them in this framework. For this
+=C2=A0 =C2=A0 reason flags that are not unique in the DPDK library are set= either to values within the
+=C2=A0 =C2=A0 RTE_MBUF_F_FIRST_FREE-RTE_MBUF_F_LAST_FREE range for Rx or s= hifted 61+ bits for Tx.
+
+=C2=A0 =C2=A0 References:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK lib: ``lib/mbuf/rte_mbuf_core.h``
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 # RX flags
+
+=C2=A0 =C2=A0 #: The RX packet is a 802.1q VLAN packet, and the tci has be= en saved in mbuf->vlan_tci. If the
+=C2=A0 =C2=A0 #: flag RTE_MBUF_F_RX_VLAN_STRIPPED is also present, the VLA= N header has been stripped from
+=C2=A0 =C2=A0 #: mbuf data, else it is still present.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_VLAN =3D auto()
+
+=C2=A0 =C2=A0 #: RX packet with RSS hash result.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_RSS_HASH =3D auto()
+
+=C2=A0 =C2=A0 #: RX packet with FDIR match indicate.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_FDIR =3D auto()
+
+=C2=A0 =C2=A0 #: This flag is set when the outermost IP header checksum is= detected as wrong by the hardware.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD =3D 1 << 5
+
+=C2=A0 =C2=A0 #: A vlan has been stripped by the hardware and its tci is s= aved in mbuf->vlan_tci. This can
+=C2=A0 =C2=A0 #: only happen if vlan stripping is enabled in the RX config= uration of the PMD. When
+=C2=A0 =C2=A0 #: RTE_MBUF_F_RX_VLAN_STRIPPED is set, RTE_MBUF_F_RX_VLAN mu= st also be set.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_VLAN_STRIPPED =3D auto()
+
+=C2=A0 =C2=A0 #: No information about the RX IP checksum. Value is 0 in th= e DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN =3D 1 << 23
+=C2=A0 =C2=A0 #: The IP checksum in the packet is wrong.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_IP_CKSUM_BAD =3D 1 << 4
+=C2=A0 =C2=A0 #: The IP checksum in the packet is valid.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_IP_CKSUM_GOOD =3D 1 << 7
+=C2=A0 =C2=A0 #: The IP checksum is not correct in the packet data, but th= e integrity of the IP header is
+=C2=A0 =C2=A0 #: verified. Value is RTE_MBUF_F_RX_IP_CKSUM_BAD | RTE_MBUF_= F_RX_IP_CKSUM_GOOD in the DPDK
+=C2=A0 =C2=A0 #: library.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_IP_CKSUM_NONE =3D 1 << 24
+
+=C2=A0 =C2=A0 #: No information about the RX L4 checksum. Value is 0 in th= e DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN =3D 1 << 25
+=C2=A0 =C2=A0 #: The L4 checksum in the packet is wrong.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_L4_CKSUM_BAD =3D 1 << 3
+=C2=A0 =C2=A0 #: The L4 checksum in the packet is valid.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_L4_CKSUM_GOOD =3D 1 << 8
+=C2=A0 =C2=A0 #: The L4 checksum is not correct in the packet data, but th= e integrity of the L4 data is
+=C2=A0 =C2=A0 #: verified. Value is RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_= F_RX_L4_CKSUM_GOOD in the DPDK
+=C2=A0 =C2=A0 #: library.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_L4_CKSUM_NONE =3D 1 << 26
+
+=C2=A0 =C2=A0 #: RX IEEE1588 L2 Ethernet PT Packet.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_IEEE1588_PTP =3D 1 << 9
+=C2=A0 =C2=A0 #: RX IEEE1588 L2/L4 timestamped packet.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_IEEE1588_TMST =3D 1 << 10
+
+=C2=A0 =C2=A0 #: FD id reported if FDIR match.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_FDIR_ID =3D 1 << 13
+=C2=A0 =C2=A0 #: Flexible bytes reported if FDIR match.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_FDIR_FLX =3D 1 << 14
+
+=C2=A0 =C2=A0 #: If both RTE_MBUF_F_RX_QINQ_STRIPPED and RTE_MBUF_F_RX_VLA= N_STRIPPED are set, the 2 VLANs
+=C2=A0 =C2=A0 #: have been stripped by the hardware. If RTE_MBUF_F_RX_QINQ= _STRIPPED is set and
+=C2=A0 =C2=A0 #: RTE_MBUF_F_RX_VLAN_STRIPPED is unset, only the outer VLAN= is removed from packet data.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_QINQ_STRIPPED =3D auto()
+
+=C2=A0 =C2=A0 #: When packets are coalesced by a hardware or virtual drive= r, this flag can be set in the RX
+=C2=A0 =C2=A0 #: mbuf, meaning that the m->tso_segsz field is valid and= is set to the segment size of
+=C2=A0 =C2=A0 #: original packets.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_LRO =3D auto()
+
+=C2=A0 =C2=A0 #: Indicate that security offload processing was applied on = the RX packet.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_SEC_OFFLOAD =3D 1 << 18
+=C2=A0 =C2=A0 #: Indicate that security offload processing failed on the R= X packet.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED =3D auto()
+
+=C2=A0 =C2=A0 #: The RX packet is a double VLAN. If this flag is set, RTE_= MBUF_F_RX_VLAN must also be set. If
+=C2=A0 =C2=A0 #: the flag RTE_MBUF_F_RX_QINQ_STRIPPED is also present, bot= h VLANs headers have been stripped
+=C2=A0 =C2=A0 #: from mbuf data, else they are still present.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_QINQ =3D auto()
+
+=C2=A0 =C2=A0 #: No info about the outer RX L4 checksum. Value is 0 in the= DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN =3D 1 << 27
+=C2=A0 =C2=A0 #: The outer L4 checksum in the packet is wrong
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD =3D 1 << 21
+=C2=A0 =C2=A0 #: The outer L4 checksum in the packet is valid
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD =3D 1 << 22
+=C2=A0 =C2=A0 #: Invalid outer L4 checksum state. Value is
+=C2=A0 =C2=A0 #: RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD | RTE_MBUF_F_RX_OUTER_L4= _CKSUM_GOOD in the DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_L4_CKSUM_INVALID =3D 1 << 28
+
+=C2=A0 =C2=A0 # TX flags
+
+=C2=A0 =C2=A0 #: Outer UDP checksum offload flag. This flag is used for en= abling outer UDP checksum in PMD.
+=C2=A0 =C2=A0 #: To use outer UDP checksum, the user either needs to enabl= e the following in mbuf:
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 #:=C2=A0 a) Fill outer_l2_len and outer_l3_len in mbuf.
+=C2=A0 =C2=A0 #:=C2=A0 b) Set the RTE_MBUF_F_TX_OUTER_UDP_CKSUM flag.
+=C2=A0 =C2=A0 #:=C2=A0 c) Set the RTE_MBUF_F_TX_OUTER_IPV4 or RTE_MBUF_F_T= X_OUTER_IPV6 flag.
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 #: Or configure RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM offload f= lag.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_OUTER_UDP_CKSUM =3D 1 << 41
+
+=C2=A0 =C2=A0 #: UDP Fragmentation Offload flag. This flag is used for ena= bling UDP fragmentation in SW or in
+=C2=A0 =C2=A0 #: HW.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_UDP_SEG =3D auto()
+
+=C2=A0 =C2=A0 #: Request security offload processing on the TX packet. To = use Tx security offload, the user
+=C2=A0 =C2=A0 #: needs to fill l2_len in mbuf indicating L2 header size an= d where L3 header starts.
+=C2=A0 =C2=A0 #: Similarly, l3_len should also be filled along with ol_fla= gs reflecting current L3 type.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_SEC_OFFLOAD =3D auto()
+
+=C2=A0 =C2=A0 #: Offload the MACsec. This flag must be set by the applicat= ion to enable this offload feature
+=C2=A0 =C2=A0 #: for a packet to be transmitted.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_MACSEC =3D auto()
+
+=C2=A0 =C2=A0 # Bits 45:48 are used for the tunnel type in ``lib/mbuf/rte_= mbuf_core.h``, but some are modified
+=C2=A0 =C2=A0 # in this Flag to maintain uniqueness. The tunnel type must = be specified for TSO or checksum on
+=C2=A0 =C2=A0 # the inner part of tunnel packets. These flags can be used = with RTE_MBUF_F_TX_TCP_SEG for TSO,
+=C2=A0 =C2=A0 # or RTE_MBUF_F_TX_xxx_CKSUM. The mbuf fields for inner and = outer header lengths are required:
+=C2=A0 =C2=A0 # outer_l2_len, outer_l3_len, l2_len, l3_len, l4_len and tso= _segsz for TSO.
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_VXLAN =3D 1 << 45
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_GRE =3D 1 << 46
+=C2=A0 =C2=A0 #: Value is 3 << 45 in the DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_IPIP =3D 1 << 61
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_GENEVE =3D 1 << 47
+=C2=A0 =C2=A0 #: TX packet with MPLS-in-UDP RFC 7510 header. Value is 5 &l= t;< 45 in the DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_MPLSINUDP =3D 1 << 62
+=C2=A0 =C2=A0 #: Value is 6 << 45 in the DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE =3D 1 << 63
+=C2=A0 =C2=A0 #: Value is 7 << 45 in the DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_GTP =3D 1 << 64
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_ESP =3D 1 << 48
+=C2=A0 =C2=A0 #: Generic IP encapsulated tunnel type, used for TSO and che= cksum offload. This can be used for
+=C2=A0 =C2=A0 #: tunnels which are not standards or listed above. It is pr= eferred to use specific tunnel
+=C2=A0 =C2=A0 #: flags like RTE_MBUF_F_TX_TUNNEL_GRE or RTE_MBUF_F_TX_TUNN= EL_IPIP if possible. The ethdev
+=C2=A0 =C2=A0 #: must be configured with RTE_ETH_TX_OFFLOAD_IP_TNL_TSO.=C2= =A0 Outer and inner checksums are done
+=C2=A0 =C2=A0 #: according to the existing flags like RTE_MBUF_F_TX_xxx_CK= SUM. Specific tunnel headers that
+=C2=A0 =C2=A0 #: contain payload length, sequence id or checksum are not e= xpected to be updated. Value is
+=C2=A0 =C2=A0 #: 0xD << 45 in the DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_IP =3D 1 << 65
+=C2=A0 =C2=A0 #: Generic UDP encapsulated tunnel type, used for TSO and ch= ecksum offload. UDP tunnel type
+=C2=A0 =C2=A0 #: implies outer IP layer. It can be used for tunnels which = are not standards or listed above.
+=C2=A0 =C2=A0 #: It is preferred to use specific tunnel flags like RTE_MBU= F_F_TX_TUNNEL_VXLAN if possible.
+=C2=A0 =C2=A0 #: The ethdev must be configured with RTE_ETH_TX_OFFLOAD_UDP= _TNL_TSO. Outer and inner checksums
+=C2=A0 =C2=A0 #: are done according to the existing flags like RTE_MBUF_F_= TX_xxx_CKSUM. Specific tunnel
+=C2=A0 =C2=A0 #: headers that contain payload length, sequence id or check= sum are not expected to be updated.
+=C2=A0 =C2=A0 #: value is 0xE << 45 in the DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_UDP =3D 1 << 66
+
+=C2=A0 =C2=A0 #: Double VLAN insertion (QinQ) request to driver, driver ma= y offload the insertion based on
+=C2=A0 =C2=A0 #: device capability. Mbuf 'vlan_tci' & 'vla= n_tci_outer' must be valid when this flag is set.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_QINQ =3D 1 << 49
+
+=C2=A0 =C2=A0 #: TCP segmentation offload. To enable this offload feature = for a packet to be transmitted on
+=C2=A0 =C2=A0 #: hardware supporting TSO:
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 #:=C2=A0 - set the RTE_MBUF_F_TX_TCP_SEG flag in mbuf->ol= _flags (this flag implies
+=C2=A0 =C2=A0 #:=C2=A0 =C2=A0 =C2=A0 RTE_MBUF_F_TX_TCP_CKSUM)
+=C2=A0 =C2=A0 #:=C2=A0 - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_= IPV6
+=C2=A0 =C2=A0 #:=C2=A0 =C2=A0 =C2=A0 * if it's IPv4, set the RTE_MBUF_= F_TX_IP_CKSUM flag
+=C2=A0 =C2=A0 #:=C2=A0 - fill the mbuf offload information: l2_len, l3_len= , l4_len, tso_segsz
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TCP_SEG =3D auto()
+
+=C2=A0 =C2=A0 #: TX IEEE1588 packet to timestamp.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_IEEE1588_TMST =3D auto()
+
+=C2=A0 =C2=A0 # Bits 52+53 used for L4 packet type with checksum enabled i= n ``lib/mbuf/rte_mbuf_core.h`` but
+=C2=A0 =C2=A0 # some values must be modified in this framework to maintain= uniqueness. To use hardware
+=C2=A0 =C2=A0 # L4 checksum offload, the user needs to:
+=C2=A0 =C2=A0 #
+=C2=A0 =C2=A0 # - fill l2_len and l3_len in mbuf
+=C2=A0 =C2=A0 # - set the flags RTE_MBUF_F_TX_TCP_CKSUM, RTE_MBUF_F_TX_SCT= P_CKSUM or
+=C2=A0 =C2=A0 #=C2=A0 =C2=A0RTE_MBUF_F_TX_UDP_CKSUM
+=C2=A0 =C2=A0 # - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6 +
+=C2=A0 =C2=A0 #: Disable L4 cksum of TX pkt. Value is 0 in the DPDK librar= y.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_L4_NO_CKSUM =3D 1 << 67
+=C2=A0 =C2=A0 #: TCP cksum of TX pkt. Computed by NIC.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_TCP_CKSUM =3D 1 << 52
+=C2=A0 =C2=A0 #: SCTP cksum of TX pkt. Computed by NIC.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_SCTP_CKSUM =3D 1 << 53
+=C2=A0 =C2=A0 #: UDP cksum of TX pkt. Computed by NIC. Value is 3 <<= 52 in the DPDK library.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_UDP_CKSUM =3D 1 << 68
+
+=C2=A0 =C2=A0 #: Offload the IP checksum in the hardware. The flag RTE_MBU= F_F_TX_IPV4 should also be set by
+=C2=A0 =C2=A0 #: the application, although a PMD will only check RTE_MBUF_= F_TX_IP_CKSUM.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_IP_CKSUM =3D 1 << 54
+
+=C2=A0 =C2=A0 #: Packet is IPv4. This flag must be set when using any offl= oad feature (TSO, L3 or L4
+=C2=A0 =C2=A0 #: checksum) to tell the NIC that the packet is an IPv4 pack= et. If the packet is a tunneled
+=C2=A0 =C2=A0 #: packet, this flag is related to the inner headers.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_IPV4 =3D auto()
+=C2=A0 =C2=A0 #: Packet is IPv6. This flag must be set when using an offlo= ad feature (TSO or L4 checksum) to
+=C2=A0 =C2=A0 #: tell the NIC that the packet is an IPv6 packet. If the pa= cket is a tunneled packet, this
+=C2=A0 =C2=A0 #: flag is related to the inner headers.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_IPV6 =3D auto()
+=C2=A0 =C2=A0 #: VLAN tag insertion request to driver, driver may offload = the insertion based on the device
+=C2=A0 =C2=A0 #: capability. mbuf 'vlan_tci' field must be valid w= hen this flag is set.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_VLAN =3D auto()
+
+=C2=A0 =C2=A0 #: Offload the IP checksum of an external header in the hard= ware. The flag
+=C2=A0 =C2=A0 #: RTE_MBUF_F_TX_OUTER_IPV4 should also be set by the applic= ation, although a PMD will only
+=C2=A0 =C2=A0 #: check RTE_MBUF_F_TX_OUTER_IP_CKSUM.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_OUTER_IP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Packet outer header is IPv4. This flag must be set when u= sing any outer offload feature (L3
+=C2=A0 =C2=A0 #: or L4 checksum) to tell the NIC that the outer header of = the tunneled packet is an IPv4
+=C2=A0 =C2=A0 #: packet.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_OUTER_IPV4 =3D auto()
+=C2=A0 =C2=A0 #: Packet outer header is IPv6. This flag must be set when u= sing any outer offload feature (L4
+=C2=A0 =C2=A0 #: checksum) to tell the NIC that the outer header of the tu= nneled packet is an IPv6 packet.
+=C2=A0 =C2=A0 RTE_MBUF_F_TX_OUTER_IPV6 =3D auto()
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def from_list_string(cls, names: str) -> Self:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag from a whitespa= ce-separated list of names.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 names: a whitespace-separated li= st containing the members of this flag.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name in names.split():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls.from_str(name)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def from_str(cls, name: str) -> Self:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag matching the su= pplied name.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name: a valid member of this fla= g in text
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 member_name =3D name.strip().replace("-&q= uot;, "_")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return cls[member_name]
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(r"ol_flags:= ([^\n]+)"),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls.from_list_string,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+
+class RtePTypes(Flag):
+=C2=A0 =C2=A0 """Flag representing possible packet types in= DPDK verbose output.
+
+=C2=A0 =C2=A0 Values in this class are derived from definitions in the RTE= MBUF ptype library in DPDK located
+=C2=A0 =C2=A0 in ``lib/mbuf/rte_mbuf_ptype.h``. Specifically, the names of= values in this class should match
+=C2=A0 =C2=A0 the possible return options from the functions ``rte_get_pty= pe_*_name`` in ``rte_mbuf_ptype.c``.
+
+=C2=A0 =C2=A0 References:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK lib: ``lib/mbuf/rte_mbuf_ptype.h``
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK ptype name formatting functions: ``lib/mb= uf/rte_mbuf_ptype.c:rte_get_ptype_*_name()``
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 # L2
+=C2=A0 =C2=A0 #: Ethernet packet type. This is used for outer packet for t= unneling cases.
+=C2=A0 =C2=A0 L2_ETHER =3D auto()
+=C2=A0 =C2=A0 #: Ethernet packet type for time sync.
+=C2=A0 =C2=A0 L2_ETHER_TIMESYNC =3D auto()
+=C2=A0 =C2=A0 #: ARP (Address Resolution Protocol) packet type.
+=C2=A0 =C2=A0 L2_ETHER_ARP =3D auto()
+=C2=A0 =C2=A0 #: LLDP (Link Layer Discovery Protocol) packet type.
+=C2=A0 =C2=A0 L2_ETHER_LLDP =3D auto()
+=C2=A0 =C2=A0 #: NSH (Network Service Header) packet type.
+=C2=A0 =C2=A0 L2_ETHER_NSH =3D auto()
+=C2=A0 =C2=A0 #: VLAN packet type.
+=C2=A0 =C2=A0 L2_ETHER_VLAN =3D auto()
+=C2=A0 =C2=A0 #: QinQ packet type.
+=C2=A0 =C2=A0 L2_ETHER_QINQ =3D auto()
+=C2=A0 =C2=A0 #: PPPOE packet type.
+=C2=A0 =C2=A0 L2_ETHER_PPPOE =3D auto()
+=C2=A0 =C2=A0 #: FCoE packet type..
+=C2=A0 =C2=A0 L2_ETHER_FCOE =3D auto()
+=C2=A0 =C2=A0 #: MPLS packet type.
+=C2=A0 =C2=A0 L2_ETHER_MPLS =3D auto()
+=C2=A0 =C2=A0 #: No L2 packet information.
+=C2=A0 =C2=A0 L2_UNKNOWN =3D auto()
+
+=C2=A0 =C2=A0 # L3
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for outer packet for tunneling
+=C2=A0 =C2=A0 #: cases, and does not contain any header option.
+=C2=A0 =C2=A0 L3_IPV4 =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for outer packet for tunneling
+=C2=A0 =C2=A0 #: cases, and contains header options.
+=C2=A0 =C2=A0 L3_IPV4_EXT =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for outer packet for tunneling
+=C2=A0 =C2=A0 #: cases, and does not contain any extension header.
+=C2=A0 =C2=A0 L3_IPV6 =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for outer packet for tunneling
+=C2=A0 =C2=A0 #: cases, and may or maynot contain header options.
+=C2=A0 =C2=A0 L3_IPV4_EXT_UNKNOWN =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for outer packet for tunneling
+=C2=A0 =C2=A0 #: cases, and contains extension headers.
+=C2=A0 =C2=A0 L3_IPV6_EXT =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for outer packet for tunneling
+=C2=A0 =C2=A0 #: cases, and may or maynot contain extension headers.
+=C2=A0 =C2=A0 L3_IPV6_EXT_UNKNOWN =3D auto()
+=C2=A0 =C2=A0 #: No L3 packet information.
+=C2=A0 =C2=A0 L3_UNKNOWN =3D auto()
+
+=C2=A0 =C2=A0 # L4
+=C2=A0 =C2=A0 #: TCP (Transmission Control Protocol) packet type. This is = used for outer packet for tunneling
+=C2=A0 =C2=A0 #: cases.
+=C2=A0 =C2=A0 L4_TCP =3D auto()
+=C2=A0 =C2=A0 #: UDP (User Datagram Protocol) packet type. This is used fo= r outer packet for tunneling cases.
+=C2=A0 =C2=A0 L4_UDP =3D auto()
+=C2=A0 =C2=A0 #: Fragmented IP (Internet Protocol) packet type. This is us= ed for outer packet for tunneling
+=C2=A0 =C2=A0 #: cases and refers to those packets of any IP types which c= an be recognized as fragmented. A
+=C2=A0 =C2=A0 #: fragmented packet cannot be recognized as any other L4 ty= pes (RTE_PTYPE_L4_TCP,
+=C2=A0 =C2=A0 #: RTE_PTYPE_L4_UDP, RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP, R= TE_PTYPE_L4_NONFRAG).
+=C2=A0 =C2=A0 L4_FRAG =3D auto()
+=C2=A0 =C2=A0 #: SCTP (Stream Control Transmission Protocol) packet type. = This is used for outer packet for
+=C2=A0 =C2=A0 #: tunneling cases.
+=C2=A0 =C2=A0 L4_SCTP =3D auto()
+=C2=A0 =C2=A0 #: ICMP (Internet Control Message Protocol) packet type. Thi= s is used for outer packet for
+=C2=A0 =C2=A0 #: tunneling cases.
+=C2=A0 =C2=A0 L4_ICMP =3D auto()
+=C2=A0 =C2=A0 #: Non-fragmented IP (Internet Protocol) packet type. This i= s used for outer packet for
+=C2=A0 =C2=A0 #: tunneling cases and refers to those packets of any IP typ= es, that cannot be recognized as
+=C2=A0 =C2=A0 #: any of the above L4 types (RTE_PTYPE_L4_TCP, RTE_PTYPE_L4= _UDP, RTE_PTYPE_L4_FRAG,
+=C2=A0 =C2=A0 #: RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP).
+=C2=A0 =C2=A0 L4_NONFRAG =3D auto()
+=C2=A0 =C2=A0 #: IGMP (Internet Group Management Protocol) packet type. +=C2=A0 =C2=A0 L4_IGMP =3D auto()
+=C2=A0 =C2=A0 #: No L4 packet information.
+=C2=A0 =C2=A0 L4_UNKNOWN =3D auto()
+
+=C2=A0 =C2=A0 # Tunnel
+=C2=A0 =C2=A0 #: IP (Internet Protocol) in IP (Internet Protocol) tunnelin= g packet type.
+=C2=A0 =C2=A0 TUNNEL_IP =3D auto()
+=C2=A0 =C2=A0 #: GRE (Generic Routing Encapsulation) tunneling packet type= .
+=C2=A0 =C2=A0 TUNNEL_GRE =3D auto()
+=C2=A0 =C2=A0 #: VXLAN (Virtual eXtensible Local Area Network) tunneling p= acket type.
+=C2=A0 =C2=A0 TUNNEL_VXLAN =3D auto()
+=C2=A0 =C2=A0 #: NVGRE (Network Virtualization using Generic Routing Encap= sulation) tunneling packet type.
+=C2=A0 =C2=A0 TUNNEL_NVGRE =3D auto()
+=C2=A0 =C2=A0 #: GENEVE (Generic Network Virtualization Encapsulation) tun= neling packet type.
+=C2=A0 =C2=A0 TUNNEL_GENEVE =3D auto()
+=C2=A0 =C2=A0 #: Tunneling packet type of Teredo, VXLAN (Virtual eXtensibl= e Local Area Network) or GRE
+=C2=A0 =C2=A0 #: (Generic Routing Encapsulation) could be recognized as th= is packet type, if they can not be
+=C2=A0 =C2=A0 #: recognized independently as of hardware capability.
+=C2=A0 =C2=A0 TUNNEL_GRENAT =3D auto()
+=C2=A0 =C2=A0 #: GTP-C (GPRS Tunnelling Protocol) control tunneling packet= type.
+=C2=A0 =C2=A0 TUNNEL_GTPC =3D auto()
+=C2=A0 =C2=A0 #: GTP-U (GPRS Tunnelling Protocol) user data tunneling pack= et type.
+=C2=A0 =C2=A0 TUNNEL_GTPU =3D auto()
+=C2=A0 =C2=A0 #: ESP (IP Encapsulating Security Payload) tunneling packet = type.
+=C2=A0 =C2=A0 TUNNEL_ESP =3D auto()
+=C2=A0 =C2=A0 #: L2TP (Layer 2 Tunneling Protocol) tunneling packet type.<= br> +=C2=A0 =C2=A0 TUNNEL_L2TP =3D auto()
+=C2=A0 =C2=A0 #: VXLAN-GPE (VXLAN Generic Protocol Extension) tunneling pa= cket type.
+=C2=A0 =C2=A0 TUNNEL_VXLAN_GPE =3D auto()
+=C2=A0 =C2=A0 #: MPLS-in-UDP tunneling packet type (RFC 7510).
+=C2=A0 =C2=A0 TUNNEL_MPLS_IN_UDP =3D auto()
+=C2=A0 =C2=A0 #: MPLS-in-GRE tunneling packet type (RFC 4023).
+=C2=A0 =C2=A0 TUNNEL_MPLS_IN_GRE =3D auto()
+=C2=A0 =C2=A0 #: No tunnel information found on the packet.
+=C2=A0 =C2=A0 TUNNEL_UNKNOWN =3D auto()
+
+=C2=A0 =C2=A0 # Inner L2
+=C2=A0 =C2=A0 #: Ethernet packet type. This is used for inner packet type = only.
+=C2=A0 =C2=A0 INNER_L2_ETHER =3D auto()
+=C2=A0 =C2=A0 #: Ethernet packet type with VLAN (Virtual Local Area Networ= k) tag.
+=C2=A0 =C2=A0 INNER_L2_ETHER_VLAN =3D auto()
+=C2=A0 =C2=A0 #: QinQ packet type.
+=C2=A0 =C2=A0 INNER_L2_ETHER_QINQ =3D auto()
+=C2=A0 =C2=A0 #: No inner L2 information found on the packet.
+=C2=A0 =C2=A0 INNER_L2_UNKNOWN =3D auto()
+
+=C2=A0 =C2=A0 # Inner L3
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for inner packet only, and does
+=C2=A0 =C2=A0 #: not contain any header option.
+=C2=A0 =C2=A0 INNER_L3_IPV4 =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for inner packet only, and
+=C2=A0 =C2=A0 #: contains header options.
+=C2=A0 =C2=A0 INNER_L3_IPV4_EXT =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for inner packet only, and does
+=C2=A0 =C2=A0 #: not contain any extension header.
+=C2=A0 =C2=A0 INNER_L3_IPV6 =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for inner packet only, and may or
+=C2=A0 =C2=A0 #: may not contain header options.
+=C2=A0 =C2=A0 INNER_L3_IPV4_EXT_UNKNOWN =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for inner packet only, and
+=C2=A0 =C2=A0 #: contains extension headers.
+=C2=A0 =C2=A0 INNER_L3_IPV6_EXT =3D auto()
+=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for inner packet only, and may or
+=C2=A0 =C2=A0 #: may not contain extension headers.
+=C2=A0 =C2=A0 INNER_L3_IPV6_EXT_UNKNOWN =3D auto()
+=C2=A0 =C2=A0 #: No inner L3 information found on the packet.
+=C2=A0 =C2=A0 INNER_L3_UNKNOWN =3D auto()
+
+=C2=A0 =C2=A0 # Inner L4
+=C2=A0 =C2=A0 #: TCP (Transmission Control Protocol) packet type. This is = used for inner packet only.
+=C2=A0 =C2=A0 INNER_L4_TCP =3D auto()
+=C2=A0 =C2=A0 #: UDP (User Datagram Protocol) packet type. This is used fo= r inner packet only.
+=C2=A0 =C2=A0 INNER_L4_UDP =3D auto()
+=C2=A0 =C2=A0 #: Fragmented IP (Internet Protocol) packet type. This is us= ed for inner packet only, and may
+=C2=A0 =C2=A0 #: or maynot have a layer 4 packet.
+=C2=A0 =C2=A0 INNER_L4_FRAG =3D auto()
+=C2=A0 =C2=A0 #: SCTP (Stream Control Transmission Protocol) packet type. = This is used for inner packet only.
+=C2=A0 =C2=A0 INNER_L4_SCTP =3D auto()
+=C2=A0 =C2=A0 #: ICMP (Internet Control Message Protocol) packet type. Thi= s is used for inner packet only.
+=C2=A0 =C2=A0 INNER_L4_ICMP =3D auto()
+=C2=A0 =C2=A0 #: Non-fragmented IP (Internet Protocol) packet type. It is = used for inner packet only, and may
+=C2=A0 =C2=A0 #: or may not have other unknown layer 4 packet types.
+=C2=A0 =C2=A0 INNER_L4_NONFRAG =3D auto()
+=C2=A0 =C2=A0 #: No inner L4 information found on the packet.
+=C2=A0 =C2=A0 INNER_L4_UNKNOWN =3D auto()
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def from_list_string(cls, names: str) -> Self:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag from a whitespa= ce-separated list of names.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 names: a whitespace-separated li= st containing the members of this flag.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name in names.split():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls.from_str(name)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def from_str(cls, name: str) -> Self:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag matching the su= pplied name.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name: a valid member of this fla= g in text
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 member_name =3D name.strip().replace("-&q= uot;, "_")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return cls[member_name]
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def make_parser(cls, hw: bool) -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hw: Whether to make a parser for= hardware ptypes or software ptypes. If :data:`True`,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hardware ptypes wi= ll be collected, otherwise software pytpes will.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(f"{'hw&= #39; if hw else 'sw'} ptype: ([^-]+)"),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls.from_list_string,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+
+@dataclass
+class TestPmdVerbosePacket(TextParser):
+=C2=A0 =C2=A0 """Packet information provided by verbose out= put in Testpmd.
+
+=C2=A0 =C2=A0 This dataclass expects that packet information be prepended = with the starting line of packet
+=C2=A0 =C2=A0 bursts. Specifically, the line that reads "port X/queue= Y: sent/received Z packets".
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #: ID of the port that handled the packet.
+=C2=A0 =C2=A0 port_id: int =3D field(metadata=3DTextParser.find_int(r"= ;port (\d+)/queue \d+"))
+=C2=A0 =C2=A0 #: ID of the queue that handled the packet.
+=C2=A0 =C2=A0 queue_id: int =3D field(metadata=3DTextParser.find_int(r&quo= t;port \d+/queue (\d+)"))
+=C2=A0 =C2=A0 #: Whether the packet was received or sent by the queue/port= .
+=C2=A0 =C2=A0 was_received: bool =3D field(metadata=3DTextParser.find(r&qu= ot;received \d+ packets"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 src_mac: str =3D field(metadata=3DTextParser.find(f"src= =3D({REGEX_FOR_MAC_ADDRESS})"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 dst_mac: str =3D field(metadata=3DTextParser.find(f"dst= =3D({REGEX_FOR_MAC_ADDRESS})"))
+=C2=A0 =C2=A0 #: Memory pool the packet was handled on.
+=C2=A0 =C2=A0 pool: str =3D field(metadata=3DTextParser.find(r"pool= =3D(\S+)"))
+=C2=A0 =C2=A0 #: Packet type in hex.
+=C2=A0 =C2=A0 p_type: int =3D field(metadata=3DTextParser.find_int(r"= type=3D(0x[a-fA-F\d]+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 length: int =3D field(metadata=3DTextParser.find_int(r"= length=3D(\d+)"))
+=C2=A0 =C2=A0 #: Number of segments in the packet.
+=C2=A0 =C2=A0 nb_segs: int =3D field(metadata=3DTextParser.find_int(r"= ;nb_segs=3D(\d+)"))
+=C2=A0 =C2=A0 #: Hardware packet type.
+=C2=A0 =C2=A0 hw_ptype: RtePTypes =3D field(metadata=3DRtePTypes.make_pars= er(hw=3DTrue))
+=C2=A0 =C2=A0 #: Software packet type.
+=C2=A0 =C2=A0 sw_ptype: RtePTypes =3D field(metadata=3DRtePTypes.make_pars= er(hw=3DFalse))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l2_len: int =3D field(metadata=3DTextParser.find_int(r"= l2_len=3D(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 ol_flags: PacketOffloadFlag =3D field(metadata=3DPacketOfflo= adFlag.make_parser())
+=C2=A0 =C2=A0 #: RSS hash of the packet in hex.
+=C2=A0 =C2=A0 rss_hash: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"RSS hash=3D(0x[a-fA-F\d]+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: RSS queue that handled the packet in hex.
+=C2=A0 =C2=A0 rss_queue: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"RSS queue=3D(0x[a-fA-F\d]+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l3_len: int | None =3D field(default=3DNone, metadata=3DText= Parser.find_int(r"l3_len=3D(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l4_len: int | None =3D field(default=3DNone, metadata=3DText= Parser.find_int(r"l4_len=3D(\d+)"))
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 l4_dport: int | None =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Destinat= ion (?:TCP|UDP) port=3D(\d+)"),
+=C2=A0 =C2=A0 )
+
+
+class RxOffloadCapability(Flag):
+=C2=A0 =C2=A0 """Rx offload capabilities of a device.
+
+=C2=A0 =C2=A0 The flags are taken from ``lib/ethdev/rte_ethdev.h``.
+=C2=A0 =C2=A0 They're prefixed with ``RTE_ETH_RX_OFFLOAD`` in ``lib/et= hdev/rte_ethdev.h``
+=C2=A0 =C2=A0 instead of ``RX_OFFLOAD``, which is what testpmd changes the= prefix to.
+=C2=A0 =C2=A0 The values are not contiguous, so the correspondence is pres= erved
+=C2=A0 =C2=A0 by specifying concrete values interspersed between auto() va= lues.
+
+=C2=A0 =C2=A0 The ``RX_OFFLOAD`` prefix has been preserved so that the sam= e flag names can be used
+=C2=A0 =C2=A0 in :class:`NicCapability`. The prefix is needed in :class:`N= icCapability` since there's
+=C2=A0 =C2=A0 no other qualifier which would sufficiently distinguish it f= rom other capabilities.
+
+=C2=A0 =C2=A0 References:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK lib: ``lib/ethdev/rte_ethdev.h``
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/cmdli= ne.c:print_rx_offloads()``
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_STRIP =3D auto()
+=C2=A0 =C2=A0 #: Device supports L3 checksum offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_IPV4_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_UDP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_TCP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports Large Receive Offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_TCP_LRO =3D auto()
+=C2=A0 =C2=A0 #: Device supports QinQ (queue in queue) offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_QINQ_STRIP =3D auto()
+=C2=A0 =C2=A0 #: Device supports inner packet L3 checksum.
+=C2=A0 =C2=A0 RX_OFFLOAD_OUTER_IPV4_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports MACsec.
+=C2=A0 =C2=A0 RX_OFFLOAD_MACSEC_STRIP =3D auto()
+=C2=A0 =C2=A0 #: Device supports filtering of a VLAN Tag identifier.
+=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_FILTER =3D 1 << 9
+=C2=A0 =C2=A0 #: Device supports VLAN offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_EXTEND =3D auto()
+=C2=A0 =C2=A0 #: Device supports receiving segmented mbufs.
+=C2=A0 =C2=A0 RX_OFFLOAD_SCATTER =3D 1 << 13
+=C2=A0 =C2=A0 #: Device supports Timestamp.
+=C2=A0 =C2=A0 RX_OFFLOAD_TIMESTAMP =3D auto()
+=C2=A0 =C2=A0 #: Device supports crypto processing while packet is receive= d in NIC.
+=C2=A0 =C2=A0 RX_OFFLOAD_SECURITY =3D auto()
+=C2=A0 =C2=A0 #: Device supports CRC stripping.
+=C2=A0 =C2=A0 RX_OFFLOAD_KEEP_CRC =3D auto()
+=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
+=C2=A0 =C2=A0 RX_OFFLOAD_SCTP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports inner packet L4 checksum.
+=C2=A0 =C2=A0 RX_OFFLOAD_OUTER_UDP_CKSUM =3D auto()
+=C2=A0 =C2=A0 #: Device supports RSS hashing.
+=C2=A0 =C2=A0 RX_OFFLOAD_RSS_HASH =3D auto()
+=C2=A0 =C2=A0 #: Device supports
+=C2=A0 =C2=A0 RX_OFFLOAD_BUFFER_SPLIT =3D auto()
+=C2=A0 =C2=A0 #: Device supports all checksum capabilities.
+=C2=A0 =C2=A0 RX_OFFLOAD_CHECKSUM =3D RX_OFFLOAD_IPV4_CKSUM | RX_OFFLOAD_U= DP_CKSUM | RX_OFFLOAD_TCP_CKSUM
+=C2=A0 =C2=A0 #: Device supports all VLAN capabilities.
+=C2=A0 =C2=A0 RX_OFFLOAD_VLAN =3D (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 RX_OFFLOAD_VLAN_STRIP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | RX_OFFLOAD_VLAN_FILTER
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | RX_OFFLOAD_VLAN_EXTEND
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 | RX_OFFLOAD_QINQ_STRIP
+=C2=A0 =C2=A0 )
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def from_string(cls, line: str) -> Self:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Make an instance from a stri= ng containing the flag names separated with a space.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 line: The line to parse.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 A new instance containing all fo= und flags.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for flag_name in line.split():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls[f"RX_OFFLOAD_= {flag_name}"]
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
+
+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def make_parser(cls, per_port: bool) -> ParserFn:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Make a parser function.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 per_port: If :data:`True`, will = return capabilities per port. If :data:`False`,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 will return capabi= lities per queue.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 granularity =3D "Port" if per_port e= lse "Queue"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(rf"Per {gra= nularity}\s+:(.*)$", re.MULTILINE),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls.from_string,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+
+
+@dataclass
+class RxOffloadCapabilities(TextParser):
+=C2=A0 =C2=A0 """The result of testpmd's ``show port &l= t;port_id> rx_offload capabilities`` command.
+
+=C2=A0 =C2=A0 References:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd command function: ``app/test-pmd/cmdli= ne.c:cmd_rx_offload_get_capa()``
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/cmdli= ne.c:cmd_rx_offload_get_capa_parsed()``
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #:
+=C2=A0 =C2=A0 port_id: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Rx Offlo= ading Capabilities of port (\d+) :")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Per-queue Rx offload capabilities.
+=C2=A0 =C2=A0 per_queue: RxOffloadCapability =3D field(metadata=3DRxOffloa= dCapability.make_parser(False))
+=C2=A0 =C2=A0 #: Capabilities other than per-queue Rx offload capabilities= .
+=C2=A0 =C2=A0 per_port: RxOffloadCapability =3D field(metadata=3DRxOffload= Capability.make_parser(True))
+
+
+@dataclass
+class TestPmdPortFlowCtrl(TextParser):
+=C2=A0 =C2=A0 """Class representing a port's flow contr= ol parameters.
+
+=C2=A0 =C2=A0 The parameters can also be parsed from the output of ``show = port <port_id> flow_ctrl``.
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 #: Enable Reactive Extensions.
+=C2=A0 =C2=A0 rx: bool =3D field(default=3DFalse, metadata=3DTextParser.fi= nd(r"Rx pause: on"))
+=C2=A0 =C2=A0 #: Enable Transmit.
+=C2=A0 =C2=A0 tx: bool =3D field(default=3DFalse, metadata=3DTextParser.fi= nd(r"Tx pause: on"))
+=C2=A0 =C2=A0 #: High threshold value to trigger XOFF.
+=C2=A0 =C2=A0 high_water: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3D0, metadata=3DTextParser.find_int(r&= quot;High waterline: (0x[a-fA-F\d]+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Low threshold value to trigger XON.
+=C2=A0 =C2=A0 low_water: int =3D field(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3D0, metadata=3DTextParser.find_int(r&= quot;Low waterline: (0x[a-fA-F\d]+)")
+=C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 #: Pause quota in the Pause frame.
+=C2=A0 =C2=A0 pause_time: int =3D field(default=3D0, metadata=3DTextParser= .find_int(r"Pause time: (0x[a-fA-F\d]+)"))
+=C2=A0 =C2=A0 #: Send XON frame.
+=C2=A0 =C2=A0 send_xon: bool =3D field(default=3DFalse, metadata=3DTextPar= ser.find(r"Tx pause: on"))
+=C2=A0 =C2=A0 #: Enable receiving MAC control frames.
+=C2=A0 =C2=A0 mac_ctrl_frame_fwd: bool =3D field(default=3DFalse, metadata= =3DTextParser.find(r"Tx pause: on"))
+=C2=A0 =C2=A0 #: Change the auto-negotiation parameter.
+=C2=A0 =C2=A0 autoneg: bool =3D field(default=3DFalse, metadata=3DTextPars= er.find(r"Autoneg: on"))
+
+=C2=A0 =C2=A0 def __str__(self) -> str:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the string represent= ation of this instance."""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D (
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"rx {'on' if self.= rx else 'off'} "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"tx {'on' if self.= tx else 'off'} "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"{self.high_water} "<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"{self.low_water} " +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"{self.pause_time} "<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"{1 if self.send_xon else = 0} "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"mac_ctrl_frame_fwd {'= on' if self.mac_ctrl_frame_fwd else 'off'} "
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"autoneg {'on' if = self.autoneg else 'off'}"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init= __.py
index 1ec744d1d4..d2f0138e4a 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -28,7 +28,6 @@
=C2=A0 =C2=A0 =C2=A0 =C2=A0and makes it thread safe should we ever want to = move in that direction.
=C2=A0"""

-import os
=C2=A0from pathlib import Path
=C2=A0from typing import TYPE_CHECKING, Annotated, Any, Literal, TypeVar, c= ast

@@ -43,7 +42,7 @@
=C2=A0from .test_run import TestRunConfiguration, create_test_suites_config= _model

=C2=A0# Import only if type checking or building docs, to prevent circular = imports.
-if TYPE_CHECKING or os.environ.get("DTS_DOC_BUILD"):
+if TYPE_CHECKING:
=C2=A0 =C2=A0 =C2=A0from framework.test_suite import BaseConfig

=C2=A0NodesConfig =3D Annotated[list[NodeConfiguration], Field(min_length= =3D1)]
diff --git a/dts/framework/params/eal.py b/dts/framework/params/eal.py
index b90ff33dcf..e84a20f02f 100644
--- a/dts/framework/params/eal.py
+++ b/dts/framework/params/eal.py
@@ -4,15 +4,17 @@
=C2=A0"""Module representing the DPDK EAL-related parameters= ."""

=C2=A0from dataclasses import dataclass, field
-from typing import Literal
+from typing import TYPE_CHECKING, Literal

=C2=A0from framework.params import Params, Switch
=C2=A0from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.port import Port
=C2=A0from framework.testbed_model.virtual_device import VirtualDevice

+if TYPE_CHECKING:
+=C2=A0 =C2=A0 from framework.testbed_model.port import Port

-def _port_to_pci(port: Port) -> str:
+
+def _port_to_pci(port: "Port") -> str:
=C2=A0 =C2=A0 =C2=A0return port.pci


@@ -42,11 +44,11 @@ class EalParams(Params):
=C2=A0 =C2=A0 =C2=A0vdevs: list[VirtualDevice] | None =3D field(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0default=3DNone, metadata=3DParams.multipl= e() | Params.long("vdev")
=C2=A0 =C2=A0 =C2=A0)
-=C2=A0 =C2=A0 allowed_ports: list[Port] | None =3D field(
+=C2=A0 =C2=A0 allowed_ports: list["Port"] | None =3D field(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0default=3DNone,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0metadata=3DParams.convert_value(_port_to_= pci) | Params.multiple() | Params.short("a"),
=C2=A0 =C2=A0 =C2=A0)
-=C2=A0 =C2=A0 blocked_ports: list[Port] | None =3D field(
+=C2=A0 =C2=A0 blocked_ports: list["Port"] | None =3D field(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0default=3DNone,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0metadata=3DParams.convert_value(_port_to_= pci) | Params.multiple() | Params.short("b"),
=C2=A0 =C2=A0 =C2=A0)
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py<= br> index 87d11502e8..5bc4bd37d9 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -15,8 +15,7 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):<= br> =C2=A0from pathlib import PurePath
=C2=A0from typing import TypedDict

-from framework.params import Switch, YesNoSwitch
-from framework.params.testpmd import (
+from api.testpmd.config import (
=C2=A0 =C2=A0 =C2=A0AnonMempoolAllocationMode,
=C2=A0 =C2=A0 =C2=A0EthPeer,
=C2=A0 =C2=A0 =C2=A0Event,
@@ -37,6 +36,7 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):<= br> =C2=A0 =C2=A0 =C2=A0TXRingParams,
=C2=A0 =C2=A0 =C2=A0TxUDPPortPair,
=C2=A0)
+from framework.params import Switch, YesNoSwitch
=C2=A0from framework.testbed_model.cpu import LogicalCoreList
=C2=A0from framework.testbed_model.port import Port
=C2=A0from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/framework/remote_session/__init__.py b/dts/framework/remot= e_session/__init__.py
index 1a5cf6abd3..394c88b25e 100644
--- a/dts/framework/remote_session/__init__.py
+++ b/dts/framework/remote_session/__init__.py
@@ -11,47 +11,3 @@
=C2=A0The interactive sessions open an interactive shell which is continuou= sly open,
=C2=A0allowing it to send and receive data within that particular shell. =C2=A0"""
-
-from framework.config.node import NodeConfiguration
-from framework.logger import DTSLogger
-
-from .interactive_remote_session import InteractiveRemoteSession
-from .remote_session import RemoteSession
-from .ssh_session import SSHSession
-
-
-def create_remote_session(
-=C2=A0 =C2=A0 node_config: NodeConfiguration, name: str, logger: DTSLogger=
-) -> RemoteSession:
-=C2=A0 =C2=A0 """Factory for non-interactive remote session= s.
-
-=C2=A0 =C2=A0 The function returns an SSH session, but will be extended if= support
-=C2=A0 =C2=A0 for other protocols is added.
-
-=C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 node_config: The test run configuration of the= node to connect to.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 name: The name of the session.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 logger: The logger instance this session will = use.
-
-=C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 The SSH remote session.
-=C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 return SSHSession(node_config, name, logger)
-
-
-def create_interactive_session(
-=C2=A0 =C2=A0 node_config: NodeConfiguration, logger: DTSLogger
-) -> InteractiveRemoteSession:
-=C2=A0 =C2=A0 """Factory for interactive remote sessions. -
-=C2=A0 =C2=A0 The function returns an interactive SSH session, but will be= extended if support
-=C2=A0 =C2=A0 for other protocols is added.
-
-=C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 node_config: The test run configuration of the= node to connect to.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 logger: The logger instance this session will = use.
-
-=C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 The interactive SSH remote session.
-=C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 return InteractiveRemoteSession(node_config, logger)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/= remote_session/testpmd_shell.py
deleted file mode 100644
index ad8cb273dc..0000000000
--- a/dts/framework/remote_session/testpmd_shell.py
+++ /dev/null
@@ -1,2844 +0,0 @@
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2023 University of New Hampshire
-# Copyright(c) 2023 PANTHEON.tech s.r.o.
-# Copyright(c) 2024 Arm Limited
-
-"""Testpmd interactive shell.
-
-Typical usage example in a TestSuite::
-
-=C2=A0 =C2=A0 testpmd_shell =3D TestPmdShell(self.sut_node)
-=C2=A0 =C2=A0 devices =3D testpmd_shell.get_devices()
-=C2=A0 =C2=A0 for device in devices:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 print(device)
-=C2=A0 =C2=A0 testpmd_shell.close()
-"""
-
-import functools
-import re
-import time
-from collections.abc import Callable, MutableSet
-from dataclasses import dataclass, field
-from enum import Flag, auto
-from os import environ
-from pathlib import PurePath
-from typing import TYPE_CHECKING, Any, ClassVar, Concatenate, Literal, Par= amSpec, Tuple, TypeAlias
-
-from framework.context import get_ctx
-from framework.remote_session.interactive_shell import only_active
-from framework.testbed_model.topology import TopologyType
-
-if TYPE_CHECKING or environ.get("DTS_DOC_BUILD"):
-=C2=A0 =C2=A0 from enum import Enum as NoAliasEnum
-else:
-=C2=A0 =C2=A0 from aenum import NoAliasEnum
-
-from typing_extensions import Self, Unpack
-
-from framework.exception import InteractiveCommandExecutionError, Internal= Error
-from framework.params.testpmd import PortTopology, SimpleForwardingModes, = TestPmdParams
-from framework.params.types import TestPmdParamsDict
-from framework.parser import ParserFn, TextParser
-from framework.remote_session.dpdk_shell import DPDKShell
-from framework.settings import SETTINGS
-from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
-
-P =3D ParamSpec("P")
-TestPmdShellMethod =3D Callable[Concatenate["TestPmdShell", P], = Any]
-
-TestPmdShellCapabilityMethod: TypeAlias =3D Callable[
-=C2=A0 =C2=A0 ["TestPmdShell", MutableSet["NicCapability&qu= ot;], MutableSet["NicCapability"]], None
-]
-
-TestPmdShellDecorator: TypeAlias =3D Callable[[TestPmdShellMethod], TestPm= dShellMethod]
-
-TestPmdShellNicCapability =3D tuple[TestPmdShellCapabilityMethod, TestPmdS= hellDecorator | None]
-
-
-class TestPmdDevice:
-=C2=A0 =C2=A0 """The data of a device that testpmd can reco= gnize.
-
-=C2=A0 =C2=A0 Attributes:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 pci_address: The PCI address of the device. -=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 pci_address: str
-
-=C2=A0 =C2=A0 def __init__(self, pci_address_line: str):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Initialize the device from t= he testpmd output line string.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pci_address_line: A line of test= pmd output that contains a device.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.pci_address =3D pci_address_line.strip().= split(": ")[1].strip()
-
-=C2=A0 =C2=A0 def __str__(self) -> str:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """The PCI address captures wha= t the device is."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self.pci_address
-
-
-class VLANOffloadFlag(Flag):
-=C2=A0 =C2=A0 """Flag representing the VLAN offload setting= s of a NIC port."""
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 STRIP =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 FILTER =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 EXTEND =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 QINQ_STRIP =3D auto()
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def from_str_dict(cls, d):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes an instance from a dic= t containing the flag member names with an "on" value.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 d: A dictionary containing the f= lag members as keys and any string value.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 A new instance of the flag.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name in cls.__members__:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if d.get(name) =3D=3D "on&q= uot;:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls[name= ]
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"VLAN offloa= d:\s+"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"strip (?P&l= t;STRIP>on|off), "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"filter (?P&= lt;FILTER>on|off), "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"extend (?P&= lt;EXTEND>on|off), "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"qinq strip = (?P<QINQ_STRIP>on|off)",
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 re.MULTILINE,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 named=3DTrue,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ),
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls.from_str_dict,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-
-class ChecksumOffloadOptions(Flag):
-=C2=A0 =C2=A0 """Flag representing checksum hardware offloa= d layer options."""
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ip =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 udp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 tcp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 sctp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 outer_ip =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 outer_udp =3D auto()
-
-
-class RSSOffloadTypesFlag(Flag):
-=C2=A0 =C2=A0 """Flag representing the RSS offload flow typ= es supported by the NIC port."""
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv4 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv4_frag =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv4_tcp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv4_udp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv4_sctp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv4_other =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_frag =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_tcp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_udp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_sctp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_other =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l2_payload =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_ex =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_tcp_ex =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_udp_ex =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 port =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 vxlan =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 geneve =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 nvgre =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_22 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 gtpu =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 eth =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 s_vlan =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 c_vlan =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 esp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ah =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l2tpv3 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 pfcp =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 pppoe =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ecpri =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 mpls =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv4_chksum =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l4_chksum =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l2tpv2 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ipv6_flow_label =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_38 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_39 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_40 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_41 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_42 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_43 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_44 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_45 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_46 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_47 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_48 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_49 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_50 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_defined_51 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_pre96 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_pre64 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_pre56 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_pre48 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_pre40 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_pre32 =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l2_dst_only =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l2_src_only =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l4_dst_only =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l4_src_only =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_dst_only =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_src_only =3D auto()
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ip =3D ipv4 | ipv4_frag | ipv4_other | ipv6 | ipv6_frag | ip= v6_other | ipv6_ex
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 udp =3D ipv4_udp | ipv6_udp | ipv6_udp_ex
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 tcp =3D ipv4_tcp | ipv6_tcp | ipv6_tcp_ex
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 sctp =3D ipv4_sctp | ipv6_sctp
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 tunnel =3D vxlan | geneve | nvgre
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 vlan =3D s_vlan | c_vlan
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 all =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 eth
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | vlan
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | ip
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | tcp
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | udp
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | sctp
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | l2_payload
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | l2tpv3
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | esp
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | ah
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | pfcp
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | gtpu
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | ecpri
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | mpls
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | l2tpv2
-=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def from_list_string(cls, names: str) -> Self:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag from a whitespa= ce-separated list of names.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 names: a whitespace-separated li= st containing the members of this flag.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name in names.split():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls.from_str(name)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def from_str(cls, name: str) -> Self:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag matching the su= pplied name.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name: a valid member of this fla= g in text
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 member_name =3D name.strip().replace("-&q= uot;, "_")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return cls[member_name]
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(r"Supported= RSS offload flow types:((?:\r?\n?=C2=A0 \S+)+)", re.MULTILINE),
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RSSOffloadTypesFlag.from_list_st= ring,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-
-class DeviceCapabilitiesFlag(Flag):
-=C2=A0 =C2=A0 """Flag representing the device capabilities.= """
-
-=C2=A0 =C2=A0 #: Device supports Rx queue setup after device started.
-=C2=A0 =C2=A0 RUNTIME_RX_QUEUE_SETUP =3D auto()
-=C2=A0 =C2=A0 #: Device supports Tx queue setup after device started.
-=C2=A0 =C2=A0 RUNTIME_TX_QUEUE_SETUP =3D auto()
-=C2=A0 =C2=A0 #: Device supports shared Rx queue among ports within Rx dom= ain and switch domain.
-=C2=A0 =C2=A0 RXQ_SHARE =3D auto()
-=C2=A0 =C2=A0 #: Device supports keeping flow rules across restart.
-=C2=A0 =C2=A0 FLOW_RULE_KEEP =3D auto()
-=C2=A0 =C2=A0 #: Device supports keeping shared flow objects across restar= t.
-=C2=A0 =C2=A0 FLOW_SHARED_OBJECT_KEEP =3D auto()
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find_int(r"Devic= e capabilities: (0x[A-Fa-f\d]+)"),
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-
-class DeviceErrorHandlingMode(StrEnum):
-=C2=A0 =C2=A0 """Enum representing the device error handlin= g mode."""
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 none =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 passive =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 proactive =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 unknown =3D auto()
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this enum from text.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(TextParser.find(r"= Device error handling mode: (\w+)"), cls)
-
-
-def make_device_private_info_parser() -> ParserFn:
-=C2=A0 =C2=A0 """Device private information parser.
-
-=C2=A0 =C2=A0 Ensures that we are not parsing invalid device private info = output.
-
-=C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `dataclasses.fi= eld` metadata argument containing a parser
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 function that parses the device = private info from the TestPmd port info output.
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 def _validate(info: str):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 info =3D info.strip()
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if info =3D=3D "none" or info.starts= with("Invalid file") or info.startswith("Failed to dump"= ;):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return None
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return info
-
-=C2=A0 =C2=A0 return TextParser.wrap(TextParser.find(r"Device private= info:\s+([\s\S]+)"), _validate)
-
-
-class RxQueueState(StrEnum):
-=C2=A0 =C2=A0 """RX queue states.
-
-=C2=A0 =C2=A0 References:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK lib: ``lib/ethdev/rte_ethdev.h``
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/confi= g.c:get_queue_state_name()``
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 stopped =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 started =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 hairpin =3D auto()
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 unknown =3D auto()
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this enum from text.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(TextParser.find(r"= Rx queue state: ([^\r\n]+)"), cls)
-
-
-@dataclass
-class TestPmdQueueInfo(TextParser):
-=C2=A0 =C2=A0 """Dataclass representation of the common par= ts of the testpmd `show rxq/txq info` commands."""
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 prefetch_threshold: int =3D field(metadata=3DTextParser.find= _int(r"prefetch threshold: (\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 host_threshold: int =3D field(metadata=3DTextParser.find_int= (r"host threshold: (\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 writeback_threshold: int =3D field(metadata=3DTextParser.fin= d_int(r"writeback threshold: (\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 free_threshold: int =3D field(metadata=3DTextParser.find_int= (r"free threshold: (\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 deferred_start: bool =3D field(metadata=3DTextParser.find(&q= uot;deferred start: on"))
-=C2=A0 =C2=A0 #: The number of RXD/TXDs is just the ring size of the queue= .
-=C2=A0 =C2=A0 ring_size: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;Number of (?:RXDs|TXDs): (\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 is_queue_started: bool =3D field(metadata=3DTextParser.find(= "queue state: started"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 burst_mode: str | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;Burst mode: ([^\r\n]+)")
-=C2=A0 =C2=A0 )
-
-
-@dataclass
-class TestPmdTxqInfo(TestPmdQueueInfo):
-=C2=A0 =C2=A0 """Representation of testpmd's ``show txq= info <port_id> <queue_id>`` command.
-
-=C2=A0 =C2=A0 References:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd command function: ``app/test-pmd/cmdli= ne.c:cmd_showqueue()``
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/confi= g.c:rx_queue_infos_display()``
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #: Ring size threshold
-=C2=A0 =C2=A0 rs_threshold: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"TX RS threshold: (\d+)\b")
-=C2=A0 =C2=A0 )
-
-
-@dataclass
-class TestPmdRxqInfo(TestPmdQueueInfo):
-=C2=A0 =C2=A0 """Representation of testpmd's ``show rxq= info <port_id> <queue_id>`` command.
-
-=C2=A0 =C2=A0 References:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd command function: ``app/test-pmd/cmdli= ne.c:cmd_showqueue()``
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/confi= g.c:rx_queue_infos_display()``
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #: Mempool used by that queue
-=C2=A0 =C2=A0 mempool: str | None =3D field(default=3DNone, metadata=3DTex= tParser.find(r"Mempool: ([^\r\n]+)"))
-=C2=A0 =C2=A0 #: Drop packets if no descriptors are available
-=C2=A0 =C2=A0 drop_packets: bool | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;RX drop packets: on")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Scattered packets Rx enabled
-=C2=A0 =C2=A0 scattered_packets: bool | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;RX scattered packets: on")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: The state of the queue
-=C2=A0 =C2=A0 queue_state: str | None =3D field(default=3DNone, metadata= =3DRxQueueState.make_parser())
-
-
-@dataclass
-class TestPmdPort(TextParser):
-=C2=A0 =C2=A0 """Dataclass representing the result of testp= md's ``show port info`` command."""
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 id: int =3D field(metadata=3DTextParser.find_int(r"Info= s for port (\d+)\b"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 device_name: str =3D field(metadata=3DTextParser.find(r"= ;Device name: ([^\r\n]+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 driver_name: str =3D field(metadata=3DTextParser.find(r"= ;Driver name: ([^\r\n]+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 socket_id: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;Connect to socket: (\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 is_link_up: bool =3D field(metadata=3DTextParser.find("= Link status: up"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 link_speed: str =3D field(metadata=3DTextParser.find(r"= Link speed: ([^\r\n]+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 is_link_full_duplex: bool =3D field(metadata=3DTextParser.fi= nd("Link duplex: full-duplex"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 is_link_autonegotiated: bool =3D field(metadata=3DTextParser= .find("Autoneg status: On"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 is_promiscuous_mode_enabled: bool =3D field(metadata=3DTextP= arser.find("Promiscuous mode: enabled"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 is_allmulticast_mode_enabled: bool =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find("Allmulticast = mode: enabled")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Maximum number of MAC addresses
-=C2=A0 =C2=A0 max_mac_addresses_num: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Maximum = number of MAC addresses: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Maximum configurable length of RX packet
-=C2=A0 =C2=A0 max_hash_mac_addresses_num: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Maximum = number of MAC addresses of hash filtering: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Minimum size of RX buffer
-=C2=A0 =C2=A0 min_rx_bufsize: int =3D field(metadata=3DTextParser.find_int= (r"Minimum size of RX buffer: (\d+)"))
-=C2=A0 =C2=A0 #: Maximum configurable length of RX packet
-=C2=A0 =C2=A0 max_rx_packet_length: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Maximum = configurable length of RX packet: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Maximum configurable size of LRO aggregated packet
-=C2=A0 =C2=A0 max_lro_packet_size: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Maximum = configurable size of LRO aggregated packet: (\d+)")
-=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 #: Current number of RX queues
-=C2=A0 =C2=A0 rx_queues_num: int =3D field(metadata=3DTextParser.find_int(= r"Current number of RX queues: (\d+)"))
-=C2=A0 =C2=A0 #: Max possible RX queues
-=C2=A0 =C2=A0 max_rx_queues_num: int =3D field(metadata=3DTextParser.find_= int(r"Max possible RX queues: (\d+)"))
-=C2=A0 =C2=A0 #: Max possible number of RXDs per queue
-=C2=A0 =C2=A0 max_queue_rxd_num: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Max poss= ible number of RXDs per queue: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Min possible number of RXDs per queue
-=C2=A0 =C2=A0 min_queue_rxd_num: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Min poss= ible number of RXDs per queue: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: RXDs number alignment
-=C2=A0 =C2=A0 rxd_alignment_num: int =3D field(metadata=3DTextParser.find_= int(r"RXDs number alignment: (\d+)"))
-
-=C2=A0 =C2=A0 #: Current number of TX queues
-=C2=A0 =C2=A0 tx_queues_num: int =3D field(metadata=3DTextParser.find_int(= r"Current number of TX queues: (\d+)"))
-=C2=A0 =C2=A0 #: Max possible TX queues
-=C2=A0 =C2=A0 max_tx_queues_num: int =3D field(metadata=3DTextParser.find_= int(r"Max possible TX queues: (\d+)"))
-=C2=A0 =C2=A0 #: Max possible number of TXDs per queue
-=C2=A0 =C2=A0 max_queue_txd_num: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Max poss= ible number of TXDs per queue: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Min possible number of TXDs per queue
-=C2=A0 =C2=A0 min_queue_txd_num: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Min poss= ible number of TXDs per queue: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: TXDs number alignment
-=C2=A0 =C2=A0 txd_alignment_num: int =3D field(metadata=3DTextParser.find_= int(r"TXDs number alignment: (\d+)"))
-=C2=A0 =C2=A0 #: Max segment number per packet
-=C2=A0 =C2=A0 max_packet_segment_num: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Max segm= ent number per packet: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Max segment number per MTU/TSO
-=C2=A0 =C2=A0 max_mtu_segment_num: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Max segm= ent number per MTU\/TSO: (\d+)")
-=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 device_capabilities: DeviceCapabilitiesFlag =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DDeviceCapabilitiesFlag.make_parser(= ),
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 device_error_handling_mode: DeviceErrorHandlingMode | None = =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DDeviceErrorHandling= Mode.make_parser()
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 device_private_info: str | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3Dmake_device_private_info_parser(),<= br> -=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 hash_key_size: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Hash key size in bytes: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 redirection_table_size: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Redirection table size: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 supported_rss_offload_flow_types: RSSOffloadTypesFlag =3D fi= eld(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DRSSOffloadTypesFlag(0), metadata=3DR= SSOffloadTypesFlag.make_parser()
-=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 mac_address: str | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;MAC address: ([A-Fa-f0-9:]+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 fw_version: str | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;Firmware-version: ([^\r\n]+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 dev_args: str | None =3D field(default=3DNone, metadata=3DTe= xtParser.find(r"Devargs: ([^\r\n]+)"))
-=C2=A0 =C2=A0 #: Socket id of the memory allocation
-=C2=A0 =C2=A0 mem_alloc_socket_id: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"memory a= llocation on the socket: (\d+)"),
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 mtu: int | None =3D field(default=3DNone, metadata=3DTextPar= ser.find_int(r"MTU: (\d+)"))
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 vlan_offload: VLANOffloadFlag | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DVLANOffloadFlag.make_parser(),
-=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 #: Maximum size of RX buffer
-=C2=A0 =C2=A0 max_rx_bufsize: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Maximum size of RX buffer: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Maximum number of VFs
-=C2=A0 =C2=A0 max_vfs_num: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Maximum number of VFs: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Maximum number of VMDq pools
-=C2=A0 =C2=A0 max_vmdq_pools_num: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Maximum number of VMDq pools: (\d+)")
-=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 switch_name: str | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find(r&q= uot;Switch name: ([\r\n]+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 switch_domain_id: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Switch domain Id: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 switch_port_id: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Switch Port Id: (\d+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 switch_rx_domain: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"Switch Rx domain: (\d+)")
-=C2=A0 =C2=A0 )
-
-
-@dataclass
-class TestPmdPortStats(TextParser):
-=C2=A0 =C2=A0 """Port statistics."""
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 port_id: int =3D field(metadata=3DTextParser.find_int(r"= ;NIC statistics for port (\d+)"))
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 rx_packets: int =3D field(metadata=3DTextParser.find_int(r&q= uot;RX-packets:\s+(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 rx_missed: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;RX-missed:\s+(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 rx_bytes: int =3D field(metadata=3DTextParser.find_int(r&quo= t;RX-bytes:\s+(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 rx_errors: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;RX-errors:\s+(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 rx_nombuf: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;RX-nombuf:\s+(\d+)"))
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 tx_packets: int =3D field(metadata=3DTextParser.find_int(r&q= uot;TX-packets:\s+(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 tx_errors: int =3D field(metadata=3DTextParser.find_int(r&qu= ot;TX-errors:\s+(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 tx_bytes: int =3D field(metadata=3DTextParser.find_int(r&quo= t;TX-bytes:\s+(\d+)"))
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 rx_pps: int =3D field(metadata=3DTextParser.find_int(r"= Rx-pps:\s+(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 rx_bps: int =3D field(metadata=3DTextParser.find_int(r"= Rx-bps:\s+(\d+)"))
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 tx_pps: int =3D field(metadata=3DTextParser.find_int(r"= Tx-pps:\s+(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 tx_bps: int =3D field(metadata=3DTextParser.find_int(r"= Tx-bps:\s+(\d+)"))
-
-
-@dataclass(kw_only=3DTrue)
-class FlowRule:
-=C2=A0 =C2=A0 """Class representation of flow rule paramete= rs.
-
-=C2=A0 =C2=A0 This class represents the parameters of any flow rule as per= the
-=C2=A0 =C2=A0 following pattern:
-
-=C2=A0 =C2=A0 [group {group_id}] [priority {level}] [ingress] [egress]
-=C2=A0 =C2=A0 [user_id {user_id}] pattern {item} [/ {item} [...]] / end -=C2=A0 =C2=A0 actions {action} [/ {action} [...]] / end
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 group_id: int | None =3D None
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 priority_level: int | None =3D None
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 direction: Literal["ingress", "egress"]<= br> -=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 user_id: int | None =3D None
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 pattern: list[str]
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 actions: list[str]
-
-=C2=A0 =C2=A0 def __str__(self) -> str:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the string represent= ation of this instance."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D ""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 pattern =3D " / ".join(self.pattern)=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 action =3D " / ".join(self.actions)<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.group_id is not None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"group {self.grou= p_id} "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.priority_level is not None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"priority {self.p= riority_level} "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"{self.direction} "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.user_id is not None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"user_id {self.us= er_id} "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"pattern {pattern} / end "=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret +=3D f"actions {action} / end" -=C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret
-
-
-class PacketOffloadFlag(Flag):
-=C2=A0 =C2=A0 """Flag representing the Packet Offload Featu= res Flags in DPDK.
-
-=C2=A0 =C2=A0 Values in this class are taken from the definitions in the R= TE MBUF core library in DPDK
-=C2=A0 =C2=A0 located in ``lib/mbuf/rte_mbuf_core.h``. It is expected that= flag values in this class will
-=C2=A0 =C2=A0 match the values they are set to in said DPDK library with o= ne exception; all values must be
-=C2=A0 =C2=A0 unique. For example, the definitions for unknown checksum fl= ags in ``rte_mbuf_core.h`` are all
-=C2=A0 =C2=A0 set to :data:`0`, but it is valuable to distinguish between = them in this framework. For this
-=C2=A0 =C2=A0 reason flags that are not unique in the DPDK library are set= either to values within the
-=C2=A0 =C2=A0 RTE_MBUF_F_FIRST_FREE-RTE_MBUF_F_LAST_FREE range for Rx or s= hifted 61+ bits for Tx.
-
-=C2=A0 =C2=A0 References:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK lib: ``lib/mbuf/rte_mbuf_core.h``
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 # RX flags
-
-=C2=A0 =C2=A0 #: The RX packet is a 802.1q VLAN packet, and the tci has be= en saved in mbuf->vlan_tci. If the
-=C2=A0 =C2=A0 #: flag RTE_MBUF_F_RX_VLAN_STRIPPED is also present, the VLA= N header has been stripped from
-=C2=A0 =C2=A0 #: mbuf data, else it is still present.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_VLAN =3D auto()
-
-=C2=A0 =C2=A0 #: RX packet with RSS hash result.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_RSS_HASH =3D auto()
-
-=C2=A0 =C2=A0 #: RX packet with FDIR match indicate.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_FDIR =3D auto()
-
-=C2=A0 =C2=A0 #: This flag is set when the outermost IP header checksum is= detected as wrong by the hardware.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD =3D 1 << 5
-
-=C2=A0 =C2=A0 #: A vlan has been stripped by the hardware and its tci is s= aved in mbuf->vlan_tci. This can
-=C2=A0 =C2=A0 #: only happen if vlan stripping is enabled in the RX config= uration of the PMD. When
-=C2=A0 =C2=A0 #: RTE_MBUF_F_RX_VLAN_STRIPPED is set, RTE_MBUF_F_RX_VLAN mu= st also be set.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_VLAN_STRIPPED =3D auto()
-
-=C2=A0 =C2=A0 #: No information about the RX IP checksum. Value is 0 in th= e DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN =3D 1 << 23
-=C2=A0 =C2=A0 #: The IP checksum in the packet is wrong.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_IP_CKSUM_BAD =3D 1 << 4
-=C2=A0 =C2=A0 #: The IP checksum in the packet is valid.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_IP_CKSUM_GOOD =3D 1 << 7
-=C2=A0 =C2=A0 #: The IP checksum is not correct in the packet data, but th= e integrity of the IP header is
-=C2=A0 =C2=A0 #: verified. Value is RTE_MBUF_F_RX_IP_CKSUM_BAD | RTE_MBUF_= F_RX_IP_CKSUM_GOOD in the DPDK
-=C2=A0 =C2=A0 #: library.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_IP_CKSUM_NONE =3D 1 << 24
-
-=C2=A0 =C2=A0 #: No information about the RX L4 checksum. Value is 0 in th= e DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN =3D 1 << 25
-=C2=A0 =C2=A0 #: The L4 checksum in the packet is wrong.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_L4_CKSUM_BAD =3D 1 << 3
-=C2=A0 =C2=A0 #: The L4 checksum in the packet is valid.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_L4_CKSUM_GOOD =3D 1 << 8
-=C2=A0 =C2=A0 #: The L4 checksum is not correct in the packet data, but th= e integrity of the L4 data is
-=C2=A0 =C2=A0 #: verified. Value is RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_= F_RX_L4_CKSUM_GOOD in the DPDK
-=C2=A0 =C2=A0 #: library.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_L4_CKSUM_NONE =3D 1 << 26
-
-=C2=A0 =C2=A0 #: RX IEEE1588 L2 Ethernet PT Packet.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_IEEE1588_PTP =3D 1 << 9
-=C2=A0 =C2=A0 #: RX IEEE1588 L2/L4 timestamped packet.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_IEEE1588_TMST =3D 1 << 10
-
-=C2=A0 =C2=A0 #: FD id reported if FDIR match.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_FDIR_ID =3D 1 << 13
-=C2=A0 =C2=A0 #: Flexible bytes reported if FDIR match.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_FDIR_FLX =3D 1 << 14
-
-=C2=A0 =C2=A0 #: If both RTE_MBUF_F_RX_QINQ_STRIPPED and RTE_MBUF_F_RX_VLA= N_STRIPPED are set, the 2 VLANs
-=C2=A0 =C2=A0 #: have been stripped by the hardware. If RTE_MBUF_F_RX_QINQ= _STRIPPED is set and
-=C2=A0 =C2=A0 #: RTE_MBUF_F_RX_VLAN_STRIPPED is unset, only the outer VLAN= is removed from packet data.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_QINQ_STRIPPED =3D auto()
-
-=C2=A0 =C2=A0 #: When packets are coalesced by a hardware or virtual drive= r, this flag can be set in the RX
-=C2=A0 =C2=A0 #: mbuf, meaning that the m->tso_segsz field is valid and= is set to the segment size of
-=C2=A0 =C2=A0 #: original packets.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_LRO =3D auto()
-
-=C2=A0 =C2=A0 #: Indicate that security offload processing was applied on = the RX packet.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_SEC_OFFLOAD =3D 1 << 18
-=C2=A0 =C2=A0 #: Indicate that security offload processing failed on the R= X packet.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED =3D auto()
-
-=C2=A0 =C2=A0 #: The RX packet is a double VLAN. If this flag is set, RTE_= MBUF_F_RX_VLAN must also be set. If
-=C2=A0 =C2=A0 #: the flag RTE_MBUF_F_RX_QINQ_STRIPPED is also present, bot= h VLANs headers have been stripped
-=C2=A0 =C2=A0 #: from mbuf data, else they are still present.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_QINQ =3D auto()
-
-=C2=A0 =C2=A0 #: No info about the outer RX L4 checksum. Value is 0 in the= DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN =3D 1 << 27
-=C2=A0 =C2=A0 #: The outer L4 checksum in the packet is wrong
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD =3D 1 << 21
-=C2=A0 =C2=A0 #: The outer L4 checksum in the packet is valid
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD =3D 1 << 22
-=C2=A0 =C2=A0 #: Invalid outer L4 checksum state. Value is
-=C2=A0 =C2=A0 #: RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD | RTE_MBUF_F_RX_OUTER_L4= _CKSUM_GOOD in the DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_RX_OUTER_L4_CKSUM_INVALID =3D 1 << 28
-
-=C2=A0 =C2=A0 # TX flags
-
-=C2=A0 =C2=A0 #: Outer UDP checksum offload flag. This flag is used for en= abling outer UDP checksum in PMD.
-=C2=A0 =C2=A0 #: To use outer UDP checksum, the user either needs to enabl= e the following in mbuf:
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 #:=C2=A0 a) Fill outer_l2_len and outer_l3_len in mbuf.
-=C2=A0 =C2=A0 #:=C2=A0 b) Set the RTE_MBUF_F_TX_OUTER_UDP_CKSUM flag.
-=C2=A0 =C2=A0 #:=C2=A0 c) Set the RTE_MBUF_F_TX_OUTER_IPV4 or RTE_MBUF_F_T= X_OUTER_IPV6 flag.
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 #: Or configure RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM offload f= lag.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_OUTER_UDP_CKSUM =3D 1 << 41
-
-=C2=A0 =C2=A0 #: UDP Fragmentation Offload flag. This flag is used for ena= bling UDP fragmentation in SW or in
-=C2=A0 =C2=A0 #: HW.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_UDP_SEG =3D auto()
-
-=C2=A0 =C2=A0 #: Request security offload processing on the TX packet. To = use Tx security offload, the user
-=C2=A0 =C2=A0 #: needs to fill l2_len in mbuf indicating L2 header size an= d where L3 header starts.
-=C2=A0 =C2=A0 #: Similarly, l3_len should also be filled along with ol_fla= gs reflecting current L3 type.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_SEC_OFFLOAD =3D auto()
-
-=C2=A0 =C2=A0 #: Offload the MACsec. This flag must be set by the applicat= ion to enable this offload feature
-=C2=A0 =C2=A0 #: for a packet to be transmitted.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_MACSEC =3D auto()
-
-=C2=A0 =C2=A0 # Bits 45:48 are used for the tunnel type in ``lib/mbuf/rte_= mbuf_core.h``, but some are modified
-=C2=A0 =C2=A0 # in this Flag to maintain uniqueness. The tunnel type must = be specified for TSO or checksum on
-=C2=A0 =C2=A0 # the inner part of tunnel packets. These flags can be used = with RTE_MBUF_F_TX_TCP_SEG for TSO,
-=C2=A0 =C2=A0 # or RTE_MBUF_F_TX_xxx_CKSUM. The mbuf fields for inner and = outer header lengths are required:
-=C2=A0 =C2=A0 # outer_l2_len, outer_l3_len, l2_len, l3_len, l4_len and tso= _segsz for TSO.
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_VXLAN =3D 1 << 45
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_GRE =3D 1 << 46
-=C2=A0 =C2=A0 #: Value is 3 << 45 in the DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_IPIP =3D 1 << 61
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_GENEVE =3D 1 << 47
-=C2=A0 =C2=A0 #: TX packet with MPLS-in-UDP RFC 7510 header. Value is 5 &l= t;< 45 in the DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_MPLSINUDP =3D 1 << 62
-=C2=A0 =C2=A0 #: Value is 6 << 45 in the DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE =3D 1 << 63
-=C2=A0 =C2=A0 #: Value is 7 << 45 in the DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_GTP =3D 1 << 64
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_ESP =3D 1 << 48
-=C2=A0 =C2=A0 #: Generic IP encapsulated tunnel type, used for TSO and che= cksum offload. This can be used for
-=C2=A0 =C2=A0 #: tunnels which are not standards or listed above. It is pr= eferred to use specific tunnel
-=C2=A0 =C2=A0 #: flags like RTE_MBUF_F_TX_TUNNEL_GRE or RTE_MBUF_F_TX_TUNN= EL_IPIP if possible. The ethdev
-=C2=A0 =C2=A0 #: must be configured with RTE_ETH_TX_OFFLOAD_IP_TNL_TSO.=C2= =A0 Outer and inner checksums are done
-=C2=A0 =C2=A0 #: according to the existing flags like RTE_MBUF_F_TX_xxx_CK= SUM. Specific tunnel headers that
-=C2=A0 =C2=A0 #: contain payload length, sequence id or checksum are not e= xpected to be updated. Value is
-=C2=A0 =C2=A0 #: 0xD << 45 in the DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_IP =3D 1 << 65
-=C2=A0 =C2=A0 #: Generic UDP encapsulated tunnel type, used for TSO and ch= ecksum offload. UDP tunnel type
-=C2=A0 =C2=A0 #: implies outer IP layer. It can be used for tunnels which = are not standards or listed above.
-=C2=A0 =C2=A0 #: It is preferred to use specific tunnel flags like RTE_MBU= F_F_TX_TUNNEL_VXLAN if possible.
-=C2=A0 =C2=A0 #: The ethdev must be configured with RTE_ETH_TX_OFFLOAD_UDP= _TNL_TSO. Outer and inner checksums
-=C2=A0 =C2=A0 #: are done according to the existing flags like RTE_MBUF_F_= TX_xxx_CKSUM. Specific tunnel
-=C2=A0 =C2=A0 #: headers that contain payload length, sequence id or check= sum are not expected to be updated.
-=C2=A0 =C2=A0 #: value is 0xE << 45 in the DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TUNNEL_UDP =3D 1 << 66
-
-=C2=A0 =C2=A0 #: Double VLAN insertion (QinQ) request to driver, driver ma= y offload the insertion based on
-=C2=A0 =C2=A0 #: device capability. Mbuf 'vlan_tci' & 'vla= n_tci_outer' must be valid when this flag is set.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_QINQ =3D 1 << 49
-
-=C2=A0 =C2=A0 #: TCP segmentation offload. To enable this offload feature = for a packet to be transmitted on
-=C2=A0 =C2=A0 #: hardware supporting TSO:
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 #:=C2=A0 - set the RTE_MBUF_F_TX_TCP_SEG flag in mbuf->ol= _flags (this flag implies
-=C2=A0 =C2=A0 #:=C2=A0 =C2=A0 =C2=A0 RTE_MBUF_F_TX_TCP_CKSUM)
-=C2=A0 =C2=A0 #:=C2=A0 - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_= IPV6
-=C2=A0 =C2=A0 #:=C2=A0 =C2=A0 =C2=A0 * if it's IPv4, set the RTE_MBUF_= F_TX_IP_CKSUM flag
-=C2=A0 =C2=A0 #:=C2=A0 - fill the mbuf offload information: l2_len, l3_len= , l4_len, tso_segsz
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TCP_SEG =3D auto()
-
-=C2=A0 =C2=A0 #: TX IEEE1588 packet to timestamp.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_IEEE1588_TMST =3D auto()
-
-=C2=A0 =C2=A0 # Bits 52+53 used for L4 packet type with checksum enabled i= n ``lib/mbuf/rte_mbuf_core.h`` but
-=C2=A0 =C2=A0 # some values must be modified in this framework to maintain= uniqueness. To use hardware
-=C2=A0 =C2=A0 # L4 checksum offload, the user needs to:
-=C2=A0 =C2=A0 #
-=C2=A0 =C2=A0 # - fill l2_len and l3_len in mbuf
-=C2=A0 =C2=A0 # - set the flags RTE_MBUF_F_TX_TCP_CKSUM, RTE_MBUF_F_TX_SCT= P_CKSUM or
-=C2=A0 =C2=A0 #=C2=A0 =C2=A0RTE_MBUF_F_TX_UDP_CKSUM
-=C2=A0 =C2=A0 # - set the flag RTE_MBUF_F_TX_IPV4 or RTE_MBUF_F_TX_IPV6 -
-=C2=A0 =C2=A0 #: Disable L4 cksum of TX pkt. Value is 0 in the DPDK librar= y.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_L4_NO_CKSUM =3D 1 << 67
-=C2=A0 =C2=A0 #: TCP cksum of TX pkt. Computed by NIC.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_TCP_CKSUM =3D 1 << 52
-=C2=A0 =C2=A0 #: SCTP cksum of TX pkt. Computed by NIC.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_SCTP_CKSUM =3D 1 << 53
-=C2=A0 =C2=A0 #: UDP cksum of TX pkt. Computed by NIC. Value is 3 <<= 52 in the DPDK library.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_UDP_CKSUM =3D 1 << 68
-
-=C2=A0 =C2=A0 #: Offload the IP checksum in the hardware. The flag RTE_MBU= F_F_TX_IPV4 should also be set by
-=C2=A0 =C2=A0 #: the application, although a PMD will only check RTE_MBUF_= F_TX_IP_CKSUM.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_IP_CKSUM =3D 1 << 54
-
-=C2=A0 =C2=A0 #: Packet is IPv4. This flag must be set when using any offl= oad feature (TSO, L3 or L4
-=C2=A0 =C2=A0 #: checksum) to tell the NIC that the packet is an IPv4 pack= et. If the packet is a tunneled
-=C2=A0 =C2=A0 #: packet, this flag is related to the inner headers.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_IPV4 =3D auto()
-=C2=A0 =C2=A0 #: Packet is IPv6. This flag must be set when using an offlo= ad feature (TSO or L4 checksum) to
-=C2=A0 =C2=A0 #: tell the NIC that the packet is an IPv6 packet. If the pa= cket is a tunneled packet, this
-=C2=A0 =C2=A0 #: flag is related to the inner headers.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_IPV6 =3D auto()
-=C2=A0 =C2=A0 #: VLAN tag insertion request to driver, driver may offload = the insertion based on the device
-=C2=A0 =C2=A0 #: capability. mbuf 'vlan_tci' field must be valid w= hen this flag is set.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_VLAN =3D auto()
-
-=C2=A0 =C2=A0 #: Offload the IP checksum of an external header in the hard= ware. The flag
-=C2=A0 =C2=A0 #: RTE_MBUF_F_TX_OUTER_IPV4 should also be set by the applic= ation, although a PMD will only
-=C2=A0 =C2=A0 #: check RTE_MBUF_F_TX_OUTER_IP_CKSUM.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_OUTER_IP_CKSUM =3D auto()
-=C2=A0 =C2=A0 #: Packet outer header is IPv4. This flag must be set when u= sing any outer offload feature (L3
-=C2=A0 =C2=A0 #: or L4 checksum) to tell the NIC that the outer header of = the tunneled packet is an IPv4
-=C2=A0 =C2=A0 #: packet.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_OUTER_IPV4 =3D auto()
-=C2=A0 =C2=A0 #: Packet outer header is IPv6. This flag must be set when u= sing any outer offload feature (L4
-=C2=A0 =C2=A0 #: checksum) to tell the NIC that the outer header of the tu= nneled packet is an IPv6 packet.
-=C2=A0 =C2=A0 RTE_MBUF_F_TX_OUTER_IPV6 =3D auto()
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def from_list_string(cls, names: str) -> Self:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag from a whitespa= ce-separated list of names.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 names: a whitespace-separated li= st containing the members of this flag.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name in names.split():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls.from_str(name)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def from_str(cls, name: str) -> Self:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag matching the su= pplied name.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name: a valid member of this fla= g in text
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 member_name =3D name.strip().replace("-&q= uot;, "_")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return cls[member_name]
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def make_parser(cls) -> ParserFn:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(r"ol_flags:= ([^\n]+)"),
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls.from_list_string,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-
-class RtePTypes(Flag):
-=C2=A0 =C2=A0 """Flag representing possible packet types in= DPDK verbose output.
-
-=C2=A0 =C2=A0 Values in this class are derived from definitions in the RTE= MBUF ptype library in DPDK located
-=C2=A0 =C2=A0 in ``lib/mbuf/rte_mbuf_ptype.h``. Specifically, the names of= values in this class should match
-=C2=A0 =C2=A0 the possible return options from the functions ``rte_get_pty= pe_*_name`` in ``rte_mbuf_ptype.c``.
-
-=C2=A0 =C2=A0 References:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK lib: ``lib/mbuf/rte_mbuf_ptype.h``
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK ptype name formatting functions: ``lib/mb= uf/rte_mbuf_ptype.c:rte_get_ptype_*_name()``
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 # L2
-=C2=A0 =C2=A0 #: Ethernet packet type. This is used for outer packet for t= unneling cases.
-=C2=A0 =C2=A0 L2_ETHER =3D auto()
-=C2=A0 =C2=A0 #: Ethernet packet type for time sync.
-=C2=A0 =C2=A0 L2_ETHER_TIMESYNC =3D auto()
-=C2=A0 =C2=A0 #: ARP (Address Resolution Protocol) packet type.
-=C2=A0 =C2=A0 L2_ETHER_ARP =3D auto()
-=C2=A0 =C2=A0 #: LLDP (Link Layer Discovery Protocol) packet type.
-=C2=A0 =C2=A0 L2_ETHER_LLDP =3D auto()
-=C2=A0 =C2=A0 #: NSH (Network Service Header) packet type.
-=C2=A0 =C2=A0 L2_ETHER_NSH =3D auto()
-=C2=A0 =C2=A0 #: VLAN packet type.
-=C2=A0 =C2=A0 L2_ETHER_VLAN =3D auto()
-=C2=A0 =C2=A0 #: QinQ packet type.
-=C2=A0 =C2=A0 L2_ETHER_QINQ =3D auto()
-=C2=A0 =C2=A0 #: PPPOE packet type.
-=C2=A0 =C2=A0 L2_ETHER_PPPOE =3D auto()
-=C2=A0 =C2=A0 #: FCoE packet type..
-=C2=A0 =C2=A0 L2_ETHER_FCOE =3D auto()
-=C2=A0 =C2=A0 #: MPLS packet type.
-=C2=A0 =C2=A0 L2_ETHER_MPLS =3D auto()
-=C2=A0 =C2=A0 #: No L2 packet information.
-=C2=A0 =C2=A0 L2_UNKNOWN =3D auto()
-
-=C2=A0 =C2=A0 # L3
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for outer packet for tunneling
-=C2=A0 =C2=A0 #: cases, and does not contain any header option.
-=C2=A0 =C2=A0 L3_IPV4 =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for outer packet for tunneling
-=C2=A0 =C2=A0 #: cases, and contains header options.
-=C2=A0 =C2=A0 L3_IPV4_EXT =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for outer packet for tunneling
-=C2=A0 =C2=A0 #: cases, and does not contain any extension header.
-=C2=A0 =C2=A0 L3_IPV6 =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for outer packet for tunneling
-=C2=A0 =C2=A0 #: cases, and may or maynot contain header options.
-=C2=A0 =C2=A0 L3_IPV4_EXT_UNKNOWN =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for outer packet for tunneling
-=C2=A0 =C2=A0 #: cases, and contains extension headers.
-=C2=A0 =C2=A0 L3_IPV6_EXT =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for outer packet for tunneling
-=C2=A0 =C2=A0 #: cases, and may or maynot contain extension headers.
-=C2=A0 =C2=A0 L3_IPV6_EXT_UNKNOWN =3D auto()
-=C2=A0 =C2=A0 #: No L3 packet information.
-=C2=A0 =C2=A0 L3_UNKNOWN =3D auto()
-
-=C2=A0 =C2=A0 # L4
-=C2=A0 =C2=A0 #: TCP (Transmission Control Protocol) packet type. This is = used for outer packet for tunneling
-=C2=A0 =C2=A0 #: cases.
-=C2=A0 =C2=A0 L4_TCP =3D auto()
-=C2=A0 =C2=A0 #: UDP (User Datagram Protocol) packet type. This is used fo= r outer packet for tunneling cases.
-=C2=A0 =C2=A0 L4_UDP =3D auto()
-=C2=A0 =C2=A0 #: Fragmented IP (Internet Protocol) packet type. This is us= ed for outer packet for tunneling
-=C2=A0 =C2=A0 #: cases and refers to those packets of any IP types which c= an be recognized as fragmented. A
-=C2=A0 =C2=A0 #: fragmented packet cannot be recognized as any other L4 ty= pes (RTE_PTYPE_L4_TCP,
-=C2=A0 =C2=A0 #: RTE_PTYPE_L4_UDP, RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP, R= TE_PTYPE_L4_NONFRAG).
-=C2=A0 =C2=A0 L4_FRAG =3D auto()
-=C2=A0 =C2=A0 #: SCTP (Stream Control Transmission Protocol) packet type. = This is used for outer packet for
-=C2=A0 =C2=A0 #: tunneling cases.
-=C2=A0 =C2=A0 L4_SCTP =3D auto()
-=C2=A0 =C2=A0 #: ICMP (Internet Control Message Protocol) packet type. Thi= s is used for outer packet for
-=C2=A0 =C2=A0 #: tunneling cases.
-=C2=A0 =C2=A0 L4_ICMP =3D auto()
-=C2=A0 =C2=A0 #: Non-fragmented IP (Internet Protocol) packet type. This i= s used for outer packet for
-=C2=A0 =C2=A0 #: tunneling cases and refers to those packets of any IP typ= es, that cannot be recognized as
-=C2=A0 =C2=A0 #: any of the above L4 types (RTE_PTYPE_L4_TCP, RTE_PTYPE_L4= _UDP, RTE_PTYPE_L4_FRAG,
-=C2=A0 =C2=A0 #: RTE_PTYPE_L4_SCTP, RTE_PTYPE_L4_ICMP).
-=C2=A0 =C2=A0 L4_NONFRAG =3D auto()
-=C2=A0 =C2=A0 #: IGMP (Internet Group Management Protocol) packet type. -=C2=A0 =C2=A0 L4_IGMP =3D auto()
-=C2=A0 =C2=A0 #: No L4 packet information.
-=C2=A0 =C2=A0 L4_UNKNOWN =3D auto()
-
-=C2=A0 =C2=A0 # Tunnel
-=C2=A0 =C2=A0 #: IP (Internet Protocol) in IP (Internet Protocol) tunnelin= g packet type.
-=C2=A0 =C2=A0 TUNNEL_IP =3D auto()
-=C2=A0 =C2=A0 #: GRE (Generic Routing Encapsulation) tunneling packet type= .
-=C2=A0 =C2=A0 TUNNEL_GRE =3D auto()
-=C2=A0 =C2=A0 #: VXLAN (Virtual eXtensible Local Area Network) tunneling p= acket type.
-=C2=A0 =C2=A0 TUNNEL_VXLAN =3D auto()
-=C2=A0 =C2=A0 #: NVGRE (Network Virtualization using Generic Routing Encap= sulation) tunneling packet type.
-=C2=A0 =C2=A0 TUNNEL_NVGRE =3D auto()
-=C2=A0 =C2=A0 #: GENEVE (Generic Network Virtualization Encapsulation) tun= neling packet type.
-=C2=A0 =C2=A0 TUNNEL_GENEVE =3D auto()
-=C2=A0 =C2=A0 #: Tunneling packet type of Teredo, VXLAN (Virtual eXtensibl= e Local Area Network) or GRE
-=C2=A0 =C2=A0 #: (Generic Routing Encapsulation) could be recognized as th= is packet type, if they can not be
-=C2=A0 =C2=A0 #: recognized independently as of hardware capability.
-=C2=A0 =C2=A0 TUNNEL_GRENAT =3D auto()
-=C2=A0 =C2=A0 #: GTP-C (GPRS Tunnelling Protocol) control tunneling packet= type.
-=C2=A0 =C2=A0 TUNNEL_GTPC =3D auto()
-=C2=A0 =C2=A0 #: GTP-U (GPRS Tunnelling Protocol) user data tunneling pack= et type.
-=C2=A0 =C2=A0 TUNNEL_GTPU =3D auto()
-=C2=A0 =C2=A0 #: ESP (IP Encapsulating Security Payload) tunneling packet = type.
-=C2=A0 =C2=A0 TUNNEL_ESP =3D auto()
-=C2=A0 =C2=A0 #: L2TP (Layer 2 Tunneling Protocol) tunneling packet type.<= br> -=C2=A0 =C2=A0 TUNNEL_L2TP =3D auto()
-=C2=A0 =C2=A0 #: VXLAN-GPE (VXLAN Generic Protocol Extension) tunneling pa= cket type.
-=C2=A0 =C2=A0 TUNNEL_VXLAN_GPE =3D auto()
-=C2=A0 =C2=A0 #: MPLS-in-UDP tunneling packet type (RFC 7510).
-=C2=A0 =C2=A0 TUNNEL_MPLS_IN_UDP =3D auto()
-=C2=A0 =C2=A0 #: MPLS-in-GRE tunneling packet type (RFC 4023).
-=C2=A0 =C2=A0 TUNNEL_MPLS_IN_GRE =3D auto()
-=C2=A0 =C2=A0 #: No tunnel information found on the packet.
-=C2=A0 =C2=A0 TUNNEL_UNKNOWN =3D auto()
-
-=C2=A0 =C2=A0 # Inner L2
-=C2=A0 =C2=A0 #: Ethernet packet type. This is used for inner packet type = only.
-=C2=A0 =C2=A0 INNER_L2_ETHER =3D auto()
-=C2=A0 =C2=A0 #: Ethernet packet type with VLAN (Virtual Local Area Networ= k) tag.
-=C2=A0 =C2=A0 INNER_L2_ETHER_VLAN =3D auto()
-=C2=A0 =C2=A0 #: QinQ packet type.
-=C2=A0 =C2=A0 INNER_L2_ETHER_QINQ =3D auto()
-=C2=A0 =C2=A0 #: No inner L2 information found on the packet.
-=C2=A0 =C2=A0 INNER_L2_UNKNOWN =3D auto()
-
-=C2=A0 =C2=A0 # Inner L3
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for inner packet only, and does
-=C2=A0 =C2=A0 #: not contain any header option.
-=C2=A0 =C2=A0 INNER_L3_IPV4 =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for inner packet only, and
-=C2=A0 =C2=A0 #: contains header options.
-=C2=A0 =C2=A0 INNER_L3_IPV4_EXT =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for inner packet only, and does
-=C2=A0 =C2=A0 #: not contain any extension header.
-=C2=A0 =C2=A0 INNER_L3_IPV6 =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 4 packet type. This is use= d for inner packet only, and may or
-=C2=A0 =C2=A0 #: may not contain header options.
-=C2=A0 =C2=A0 INNER_L3_IPV4_EXT_UNKNOWN =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for inner packet only, and
-=C2=A0 =C2=A0 #: contains extension headers.
-=C2=A0 =C2=A0 INNER_L3_IPV6_EXT =3D auto()
-=C2=A0 =C2=A0 #: IP (Internet Protocol) version 6 packet type. This is use= d for inner packet only, and may or
-=C2=A0 =C2=A0 #: may not contain extension headers.
-=C2=A0 =C2=A0 INNER_L3_IPV6_EXT_UNKNOWN =3D auto()
-=C2=A0 =C2=A0 #: No inner L3 information found on the packet.
-=C2=A0 =C2=A0 INNER_L3_UNKNOWN =3D auto()
-
-=C2=A0 =C2=A0 # Inner L4
-=C2=A0 =C2=A0 #: TCP (Transmission Control Protocol) packet type. This is = used for inner packet only.
-=C2=A0 =C2=A0 INNER_L4_TCP =3D auto()
-=C2=A0 =C2=A0 #: UDP (User Datagram Protocol) packet type. This is used fo= r inner packet only.
-=C2=A0 =C2=A0 INNER_L4_UDP =3D auto()
-=C2=A0 =C2=A0 #: Fragmented IP (Internet Protocol) packet type. This is us= ed for inner packet only, and may
-=C2=A0 =C2=A0 #: or maynot have a layer 4 packet.
-=C2=A0 =C2=A0 INNER_L4_FRAG =3D auto()
-=C2=A0 =C2=A0 #: SCTP (Stream Control Transmission Protocol) packet type. = This is used for inner packet only.
-=C2=A0 =C2=A0 INNER_L4_SCTP =3D auto()
-=C2=A0 =C2=A0 #: ICMP (Internet Control Message Protocol) packet type. Thi= s is used for inner packet only.
-=C2=A0 =C2=A0 INNER_L4_ICMP =3D auto()
-=C2=A0 =C2=A0 #: Non-fragmented IP (Internet Protocol) packet type. It is = used for inner packet only, and may
-=C2=A0 =C2=A0 #: or may not have other unknown layer 4 packet types.
-=C2=A0 =C2=A0 INNER_L4_NONFRAG =3D auto()
-=C2=A0 =C2=A0 #: No inner L4 information found on the packet.
-=C2=A0 =C2=A0 INNER_L4_UNKNOWN =3D auto()
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def from_list_string(cls, names: str) -> Self:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag from a whitespa= ce-separated list of names.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 names: a whitespace-separated li= st containing the members of this flag.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name in names.split():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls.from_str(name)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def from_str(cls, name: str) -> Self:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a flag matching the su= pplied name.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name: a valid member of this fla= g in text
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of this flag.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 member_name =3D name.strip().replace("-&q= uot;, "_")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return cls[member_name]
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def make_parser(cls, hw: bool) -> ParserFn:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Makes a parser function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hw: Whether to make a parser for= hardware ptypes or software ptypes. If :data:`True`,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hardware ptypes wi= ll be collected, otherwise software pytpes will.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(f"{'hw&= #39; if hw else 'sw'} ptype: ([^-]+)"),
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls.from_list_string,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-
-@dataclass
-class TestPmdVerbosePacket(TextParser):
-=C2=A0 =C2=A0 """Packet information provided by verbose out= put in Testpmd.
-
-=C2=A0 =C2=A0 This dataclass expects that packet information be prepended = with the starting line of packet
-=C2=A0 =C2=A0 bursts. Specifically, the line that reads "port X/queue= Y: sent/received Z packets".
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #: ID of the port that handled the packet.
-=C2=A0 =C2=A0 port_id: int =3D field(metadata=3DTextParser.find_int(r"= ;port (\d+)/queue \d+"))
-=C2=A0 =C2=A0 #: ID of the queue that handled the packet.
-=C2=A0 =C2=A0 queue_id: int =3D field(metadata=3DTextParser.find_int(r&quo= t;port \d+/queue (\d+)"))
-=C2=A0 =C2=A0 #: Whether the packet was received or sent by the queue/port= .
-=C2=A0 =C2=A0 was_received: bool =3D field(metadata=3DTextParser.find(r&qu= ot;received \d+ packets"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 src_mac: str =3D field(metadata=3DTextParser.find(f"src= =3D({REGEX_FOR_MAC_ADDRESS})"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 dst_mac: str =3D field(metadata=3DTextParser.find(f"dst= =3D({REGEX_FOR_MAC_ADDRESS})"))
-=C2=A0 =C2=A0 #: Memory pool the packet was handled on.
-=C2=A0 =C2=A0 pool: str =3D field(metadata=3DTextParser.find(r"pool= =3D(\S+)"))
-=C2=A0 =C2=A0 #: Packet type in hex.
-=C2=A0 =C2=A0 p_type: int =3D field(metadata=3DTextParser.find_int(r"= type=3D(0x[a-fA-F\d]+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 length: int =3D field(metadata=3DTextParser.find_int(r"= length=3D(\d+)"))
-=C2=A0 =C2=A0 #: Number of segments in the packet.
-=C2=A0 =C2=A0 nb_segs: int =3D field(metadata=3DTextParser.find_int(r"= ;nb_segs=3D(\d+)"))
-=C2=A0 =C2=A0 #: Hardware packet type.
-=C2=A0 =C2=A0 hw_ptype: RtePTypes =3D field(metadata=3DRtePTypes.make_pars= er(hw=3DTrue))
-=C2=A0 =C2=A0 #: Software packet type.
-=C2=A0 =C2=A0 sw_ptype: RtePTypes =3D field(metadata=3DRtePTypes.make_pars= er(hw=3DFalse))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l2_len: int =3D field(metadata=3DTextParser.find_int(r"= l2_len=3D(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 ol_flags: PacketOffloadFlag =3D field(metadata=3DPacketOfflo= adFlag.make_parser())
-=C2=A0 =C2=A0 #: RSS hash of the packet in hex.
-=C2=A0 =C2=A0 rss_hash: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"RSS hash=3D(0x[a-fA-F\d]+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: RSS queue that handled the packet in hex.
-=C2=A0 =C2=A0 rss_queue: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone, metadata=3DTextParser.find_int= (r"RSS queue=3D(0x[a-fA-F\d]+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l3_len: int | None =3D field(default=3DNone, metadata=3DText= Parser.find_int(r"l3_len=3D(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l4_len: int | None =3D field(default=3DNone, metadata=3DText= Parser.find_int(r"l4_len=3D(\d+)"))
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 l4_dport: int | None =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3DNone,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Destinat= ion (?:TCP|UDP) port=3D(\d+)"),
-=C2=A0 =C2=A0 )
-
-
-class RxOffloadCapability(Flag):
-=C2=A0 =C2=A0 """Rx offload capabilities of a device.
-
-=C2=A0 =C2=A0 The flags are taken from ``lib/ethdev/rte_ethdev.h``.
-=C2=A0 =C2=A0 They're prefixed with ``RTE_ETH_RX_OFFLOAD`` in ``lib/et= hdev/rte_ethdev.h``
-=C2=A0 =C2=A0 instead of ``RX_OFFLOAD``, which is what testpmd changes the= prefix to.
-=C2=A0 =C2=A0 The values are not contiguous, so the correspondence is pres= erved
-=C2=A0 =C2=A0 by specifying concrete values interspersed between auto() va= lues.
-
-=C2=A0 =C2=A0 The ``RX_OFFLOAD`` prefix has been preserved so that the sam= e flag names can be used
-=C2=A0 =C2=A0 in :class:`NicCapability`. The prefix is needed in :class:`N= icCapability` since there's
-=C2=A0 =C2=A0 no other qualifier which would sufficiently distinguish it f= rom other capabilities.
-
-=C2=A0 =C2=A0 References:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 DPDK lib: ``lib/ethdev/rte_ethdev.h``
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/cmdli= ne.c:print_rx_offloads()``
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_STRIP =3D auto()
-=C2=A0 =C2=A0 #: Device supports L3 checksum offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_IPV4_CKSUM =3D auto()
-=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_UDP_CKSUM =3D auto()
-=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_TCP_CKSUM =3D auto()
-=C2=A0 =C2=A0 #: Device supports Large Receive Offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_TCP_LRO =3D auto()
-=C2=A0 =C2=A0 #: Device supports QinQ (queue in queue) offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_QINQ_STRIP =3D auto()
-=C2=A0 =C2=A0 #: Device supports inner packet L3 checksum.
-=C2=A0 =C2=A0 RX_OFFLOAD_OUTER_IPV4_CKSUM =3D auto()
-=C2=A0 =C2=A0 #: Device supports MACsec.
-=C2=A0 =C2=A0 RX_OFFLOAD_MACSEC_STRIP =3D auto()
-=C2=A0 =C2=A0 #: Device supports filtering of a VLAN Tag identifier.
-=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_FILTER =3D 1 << 9
-=C2=A0 =C2=A0 #: Device supports VLAN offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_EXTEND =3D auto()
-=C2=A0 =C2=A0 #: Device supports receiving segmented mbufs.
-=C2=A0 =C2=A0 RX_OFFLOAD_SCATTER =3D 1 << 13
-=C2=A0 =C2=A0 #: Device supports Timestamp.
-=C2=A0 =C2=A0 RX_OFFLOAD_TIMESTAMP =3D auto()
-=C2=A0 =C2=A0 #: Device supports crypto processing while packet is receive= d in NIC.
-=C2=A0 =C2=A0 RX_OFFLOAD_SECURITY =3D auto()
-=C2=A0 =C2=A0 #: Device supports CRC stripping.
-=C2=A0 =C2=A0 RX_OFFLOAD_KEEP_CRC =3D auto()
-=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_SCTP_CKSUM =3D auto()
-=C2=A0 =C2=A0 #: Device supports inner packet L4 checksum.
-=C2=A0 =C2=A0 RX_OFFLOAD_OUTER_UDP_CKSUM =3D auto()
-=C2=A0 =C2=A0 #: Device supports RSS hashing.
-=C2=A0 =C2=A0 RX_OFFLOAD_RSS_HASH =3D auto()
-=C2=A0 =C2=A0 #: Device supports
-=C2=A0 =C2=A0 RX_OFFLOAD_BUFFER_SPLIT =3D auto()
-=C2=A0 =C2=A0 #: Device supports all checksum capabilities.
-=C2=A0 =C2=A0 RX_OFFLOAD_CHECKSUM =3D RX_OFFLOAD_IPV4_CKSUM | RX_OFFLOAD_U= DP_CKSUM | RX_OFFLOAD_TCP_CKSUM
-=C2=A0 =C2=A0 #: Device supports all VLAN capabilities.
-=C2=A0 =C2=A0 RX_OFFLOAD_VLAN =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 RX_OFFLOAD_VLAN_STRIP
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | RX_OFFLOAD_VLAN_FILTER
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | RX_OFFLOAD_VLAN_EXTEND
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 | RX_OFFLOAD_QINQ_STRIP
-=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def from_string(cls, line: str) -> Self:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Make an instance from a stri= ng containing the flag names separated with a space.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 line: The line to parse.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 A new instance containing all fo= und flags.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag =3D cls(0)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for flag_name in line.split():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flag |=3D cls[f"RX_OFFLOAD_= {flag_name}"]
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return flag
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def make_parser(cls, per_port: bool) -> ParserFn:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Make a parser function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 per_port: If :data:`True`, will = return capabilities per port. If :data:`False`,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 will return capabi= lities per queue.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ParserFn: A dictionary for the `= dataclasses.field` metadata argument containing a
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 parser function th= at makes an instance of this flag from text.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 granularity =3D "Port" if per_port e= lse "Queue"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TextParser.wrap(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TextParser.find(rf"Per {gra= nularity}\s+:(.*)$", re.MULTILINE),
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cls.from_string,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-
-@dataclass
-class RxOffloadCapabilities(TextParser):
-=C2=A0 =C2=A0 """The result of testpmd's ``show port &l= t;port_id> rx_offload capabilities`` command.
-
-=C2=A0 =C2=A0 References:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd command function: ``app/test-pmd/cmdli= ne.c:cmd_rx_offload_get_capa()``
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd display function: ``app/test-pmd/cmdli= ne.c:cmd_rx_offload_get_capa_parsed()``
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 port_id: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 metadata=3DTextParser.find_int(r"Rx Offlo= ading Capabilities of port (\d+) :")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Per-queue Rx offload capabilities.
-=C2=A0 =C2=A0 per_queue: RxOffloadCapability =3D field(metadata=3DRxOffloa= dCapability.make_parser(False))
-=C2=A0 =C2=A0 #: Capabilities other than per-queue Rx offload capabilities= .
-=C2=A0 =C2=A0 per_port: RxOffloadCapability =3D field(metadata=3DRxOffload= Capability.make_parser(True))
-
-
-@dataclass
-class TestPmdPortFlowCtrl(TextParser):
-=C2=A0 =C2=A0 """Class representing a port's flow contr= ol parameters.
-
-=C2=A0 =C2=A0 The parameters can also be parsed from the output of ``show = port <port_id> flow_ctrl``.
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #: Enable Reactive Extensions.
-=C2=A0 =C2=A0 rx: bool =3D field(default=3DFalse, metadata=3DTextParser.fi= nd(r"Rx pause: on"))
-=C2=A0 =C2=A0 #: Enable Transmit.
-=C2=A0 =C2=A0 tx: bool =3D field(default=3DFalse, metadata=3DTextParser.fi= nd(r"Tx pause: on"))
-=C2=A0 =C2=A0 #: High threshold value to trigger XOFF.
-=C2=A0 =C2=A0 high_water: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3D0, metadata=3DTextParser.find_int(r&= quot;High waterline: (0x[a-fA-F\d]+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Low threshold value to trigger XON.
-=C2=A0 =C2=A0 low_water: int =3D field(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 default=3D0, metadata=3DTextParser.find_int(r&= quot;Low waterline: (0x[a-fA-F\d]+)")
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Pause quota in the Pause frame.
-=C2=A0 =C2=A0 pause_time: int =3D field(default=3D0, metadata=3DTextParser= .find_int(r"Pause time: (0x[a-fA-F\d]+)"))
-=C2=A0 =C2=A0 #: Send XON frame.
-=C2=A0 =C2=A0 send_xon: bool =3D field(default=3DFalse, metadata=3DTextPar= ser.find(r"Tx pause: on"))
-=C2=A0 =C2=A0 #: Enable receiving MAC control frames.
-=C2=A0 =C2=A0 mac_ctrl_frame_fwd: bool =3D field(default=3DFalse, metadata= =3DTextParser.find(r"Tx pause: on"))
-=C2=A0 =C2=A0 #: Change the auto-negotiation parameter.
-=C2=A0 =C2=A0 autoneg: bool =3D field(default=3DFalse, metadata=3DTextPars= er.find(r"Autoneg: on"))
-
-=C2=A0 =C2=A0 def __str__(self) -> str:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the string represent= ation of this instance."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"rx {'on' if self.= rx else 'off'} "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"tx {'on' if self.= tx else 'off'} "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"{self.high_water} "<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"{self.low_water} " -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"{self.pause_time} "<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"{1 if self.send_xon else = 0} "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"mac_ctrl_frame_fwd {'= on' if self.mac_ctrl_frame_fwd else 'off'} "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"autoneg {'on' if = self.autoneg else 'off'}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret
-
-
-def requires_stopped_ports(func: TestPmdShellMethod) -> TestPmdShellMet= hod:
-=C2=A0 =C2=A0 """Decorator for :class:`TestPmdShell` comman= ds methods that require stopped ports.
-
-=C2=A0 =C2=A0 If the decorated method is called while the ports are starte= d, then these are stopped before
-=C2=A0 =C2=A0 continuing.
-
-=C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 func: The :class:`TestPmdShell` method to deco= rate.
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 @functools.wraps(func)
-=C2=A0 =C2=A0 def _wrapper(self: "TestPmdShell", *args: P.args, = **kwargs: P.kwargs):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.ports_started:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Ports n= eed to be stopped to continue.")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.stop_all_ports()
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return func(self, *args, **kwargs)
-
-=C2=A0 =C2=A0 return _wrapper
-
-
-def requires_started_ports(func: TestPmdShellMethod) -> TestPmdShellMet= hod:
-=C2=A0 =C2=A0 """Decorator for :class:`TestPmdShell` comman= ds methods that require started ports.
-
-=C2=A0 =C2=A0 If the decorated method is called while the ports are stoppe= d, then these are started before
-=C2=A0 =C2=A0 continuing.
-
-=C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 func: The :class:`TestPmdShell` method to deco= rate.
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 @functools.wraps(func)
-=C2=A0 =C2=A0 def _wrapper(self: "TestPmdShell", *args: P.args, = **kwargs: P.kwargs):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not self.ports_started:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Ports n= eed to be started to continue.")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.start_all_ports()
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return func(self, *args, **kwargs)
-
-=C2=A0 =C2=A0 return _wrapper
-
-
-def add_remove_mtu(mtu: int =3D 1500) -> Callable[[TestPmdShellMethod],= TestPmdShellMethod]:
-=C2=A0 =C2=A0 """Configure MTU to `mtu` on all ports, run t= he decorated function, then revert.
-
-=C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 mtu: The MTU to configure all ports on.
-
-=C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 The method decorated with setting and revertin= g MTU.
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 def decorator(func: TestPmdShellMethod) -> TestPmdShellMe= thod:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 @functools.wraps(func)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 def wrapper(self: "TestPmdShell", *a= rgs: P.args, **kwargs: P.kwargs):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 original_mtu =3D self.ports[0].m= tu
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.set_port_mtu_all(mtu=3Dmtu,= verify=3DFalse)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 retval =3D func(self, *args, **k= wargs)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.set_port_mtu_all(original_m= tu if original_mtu else 1500, verify=3DFalse)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return retval
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return wrapper
-
-=C2=A0 =C2=A0 return decorator
-
-
-class TestPmdShell(DPDKShell):
-=C2=A0 =C2=A0 """Testpmd interactive shell.
-
-=C2=A0 =C2=A0 The testpmd shell users should never use
-=C2=A0 =C2=A0 the :meth:`~.interactive_shell.InteractiveShell.send_command= ` method directly, but rather
-=C2=A0 =C2=A0 call specialized methods. If there isn't one that satisf= ies a need, it should be added.
-
-=C2=A0 =C2=A0 Attributes:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 ports_started: Indicates whether the ports are= started.
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 _app_params: TestPmdParams
-=C2=A0 =C2=A0 _ports: list[TestPmdPort] | None
-
-=C2=A0 =C2=A0 #: The testpmd's prompt.
-=C2=A0 =C2=A0 _default_prompt: ClassVar[str] =3D "testpmd>" -
-=C2=A0 =C2=A0 #: This forces the prompt to appear after sending a command.=
-=C2=A0 =C2=A0 _command_extra_chars: ClassVar[str] =3D "\n"
-
-=C2=A0 =C2=A0 ports_started: bool
-
-=C2=A0 =C2=A0 def __init__(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 name: str | None =3D None,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 privileged: bool =3D True,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 **app_params: Unpack[TestPmdParamsDict],
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Overrides :meth:`~.dpdk_shel= l.DPDKShell.__init__`. Changes app_params to kwargs."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "port_topology" not in app_params= and get_ctx().topology.type is TopologyType.one_link:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 app_params["port_topology&q= uot;] =3D PortTopology.loop
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 super().__init__(name, privileged, app_params= =3DTestPmdParams(**app_params))
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.ports_started =3D not self._app_params.di= sable_device_start
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._ports =3D None
-
-=C2=A0 =C2=A0 @property
-=C2=A0 =C2=A0 def path(self) -> PurePath:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """The path to the testpmd exec= utable."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return PurePath("app/dpdk-testpmd")<= br> -
-=C2=A0 =C2=A0 @property
-=C2=A0 =C2=A0 def ports(self) -> list[TestPmdPort]:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """The ports of the instance. -
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 This caches the ports returned by :meth:`show_= port_info_all`.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 To force an update of port information, execut= e :meth:`show_port_info_all` or
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 :meth:`show_port_info`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns: The list of known testpmd ports.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self._ports is None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return self.show_port_info_all()=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self._ports
-
-=C2=A0 =C2=A0 @requires_started_ports
-=C2=A0 =C2=A0 def start(self, verify: bool =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Start packet forwarding with= the current configuration.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` , a seco= nd start command will be sent in an attempt to verify
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 packet forwarding = started as expected.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and forwarding fails to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 start or ports fai= l to come up.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command("start")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # If forwarding was already star= ted, sending "start" again should tell us
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 start_cmd_output =3D self.send_c= ommand("start")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Packet forwarding alrea= dy started" not in start_cmd_output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to start packet forwarding: \n{start_cmd_output}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError("Testpmd failed to start packet forwarding."= )
-
-=C2=A0 =C2=A0 def stop(self, verify: bool =3D True) -> str:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Stop packet forwarding.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` , the ou= tput of the stop command is scanned to verify that
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 forwarding was sto= pped successfully or not started. If neither is found, it is
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 considered an erro= r.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the command to stop
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 forwarding results= in an error.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Output gathered from the stop co= mmand and all other preceding logs in the buffer. This
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 output is most often used to vie= w forwarding statistics that are displayed when this
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 command is sent as well as any v= erbose packet information that hasn't been consumed
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 prior to calling this method. -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 stop_cmd_output =3D self.send_command("st= op")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "Done." = not in stop_cmd_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 and "Packet f= orwarding not started" not in stop_cmd_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to stop packet forwarding: \n{stop_cmd_output}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError("Testpmd failed to stop packet forwarding.")=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return stop_cmd_output
-
-=C2=A0 =C2=A0 def get_devices(self) -> list[TestPmdDevice]:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get a list of device names t= hat are known to testpmd.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Uses the device info listed in testpmd and the= n parses the output.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 A list of devices.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_info: str =3D self.send_command("show= device info all")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_list: list[TestPmdDevice] =3D []
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for line in dev_info.split("\n"): -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "device name:" in l= ine.lower():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_list.append(Te= stPmdDevice(line))
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return dev_list
-
-=C2=A0 =C2=A0 def wait_link_status_up(self, port_id: int, timeout=3DSETTIN= GS.timeout) -> bool:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Wait until the link status o= n the given port is "up".
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Arguments:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: Port to check the link = status on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 timeout: Time to wait for the li= nk to come up. The default value for this
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 argument may be mo= dified using the :option:`--timeout` command-line argument
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or the :envvar:`DT= S_TIMEOUT` environment variable.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Whether the link came up in time= or not.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 time_to_stop =3D time.time() + timeout
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_info: str =3D ""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 while time.time() < time_to_stop:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_info =3D self.send_command(= f"show port info {port_id}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Link status: up" i= n port_info:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 time.sleep(0.5)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.error(f"The li= nk for port {port_id} did not come up in the given timeout.")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return "Link status: up" in port_inf= o
-
-=C2=A0 =C2=A0 def set_forward_mode(self, mode: SimpleForwardingModes, veri= fy: bool =3D True):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set packet forwarding mode.<= br> -
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode: The forwarding mode to use= .
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the command will be scanned in an attempt to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify that the fo= rwarding mode was set to `mode` properly.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the forwarding mode
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to update. -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 set_fwd_output =3D self.send_command(f"se= t fwd {mode.value}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if f"Set {mode.value} packe= t forwarding mode" not in set_fwd_output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to set fwd mode to {mode.value}:\n{set_fwd_output}") -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Test pmd failed to set fwd mode to {mode.value}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def stop_all_ports(self, verify: bool =3D True) -> None:<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Stops all the ports.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command will be checked for a successful
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 execution.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the ports were not
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stopped successful= ly.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Stopping all the port= s...")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command("port stop a= ll")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify and not output.strip().endswith(&quo= t;Done"):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("Ports were not stopped successfully.")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.ports_started =3D False
-
-=C2=A0 =C2=A0 def start_all_ports(self, verify: bool =3D True) -> None:=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Starts all the ports.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command will be checked for a successful
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 execution.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the ports were not
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 started successful= ly.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Starting all the port= s...")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command("port start = all")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify and not output.strip().endswith(&quo= t;Done"):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("Ports were not started successfully.")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.ports_started =3D True
-
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def set_ports_queues(self, number_of: int) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Sets the number of queues pe= r port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 number_of: The number of RX/TX q= ueues to create per port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InternalError: If `number_of` is= invalid.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if number_of < 1:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InternalError("The nu= mber of queues must be positive and non-zero.")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port config all rxq {= number_of}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port config all txq {= number_of}")
-
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def close_all_ports(self, verify: bool =3D True) -> None:=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Close all ports.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the close command will be scanned in an attempt
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 to verify that all= ports were stopped successfully. Defaults to :data:`True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and at lease one port
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 failed to close. -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_close_output =3D self.send_command("= port close all")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 num_ports =3D len(self.ports) -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if not all(f"Port {p_id} is= closed" in port_close_output for p_id in range(num_ports)):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError("Ports were not closed successfully.")
-
-=C2=A0 =C2=A0 def show_port_info_all(self) -> list[TestPmdPort]:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the information of a= ll the ports.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 list[TestPmdPort]: A list contai= ning all the ports information as `TestPmdPort`.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command("show port i= nfo all")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Sample output of the "all" command= looks like:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 # <start>
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0********************* Infos for = port 0 *********************
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0Key: value
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0********************* Infos for = port 1 *********************
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0Key: value
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 # <end>
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Takes advantage of the double new line in be= tween ports as end delimiter. But we need to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 # artificially add a new line at the end to pi= ck up the last port. Because commands are
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 # executed on a pseudo-terminal created by par= amiko on the remote node, lines end with CRLF.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Therefore we also need to take the carriage = return into account.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 iter =3D re.finditer(r"\*{21}.*?[\r\n]{4}= ", output + "\r\n", re.S)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._ports =3D [TestPmdPort.parse(block.group= (0)) for block in iter]
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self._ports
-
-=C2=A0 =C2=A0 def show_port_info(self, port_id: int) -> TestPmdPort: -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the given port infor= mation.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port ID to gather i= nformation for.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `port_id` is invalid.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdPort: An instance of `Tes= tPmdPort` containing the given port's information.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"show port = info {port_id}", skip_first_line=3DTrue)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if output.startswith("Invalid port")= :
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("invalid port given")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 port =3D TestPmdPort.parse(output)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._update_port(port)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return port
-
-=C2=A0 =C2=A0 def _update_port(self, port: TestPmdPort) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if self._ports:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._ports =3D [
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 existing_port if <= a href=3D"http://port.id" rel=3D"noreferrer" target=3D"_blank">port.id = !=3D existing_port.id else port
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for existing_port = in self._ports
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ]
-
-=C2=A0 =C2=A0 def set_mac_addr(self, port_id: int, mac_address: str, add: = bool, verify: bool =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add or remove a mac address = on a given port's Allowlist.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port ID the mac add= ress is set on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mac_address: The mac address to = be added to or removed from the specified port.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add: If :data:`True`, add the sp= ecified mac address. If :data:`False`, remove specified
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mac address.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:'True',= assert that the 'mac_addr' operation was successful. If
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 :data:'False&#= 39;, run the command and skip this assertion.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If the set mac address operation fails.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 mac_cmd =3D "add" if add else "= remove"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"mac_addr {= mac_cmd} {port_id} {mac_address}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Bad arguments" in output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Invalid= argument provided to mac_addr")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("Invalid argument provided")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "mac_addr_cmd error:&quo= t; in output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to {mac_cmd} {mac_address} on port {port_id}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to {mac_cmd} {mac_address} on port {port_id} \n{output}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def set_multicast_mac_addr(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, multi_addr: str, add: bool= , verify: bool =3D True
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add or remove multicast mac = address to a specified port's allow list.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port ID the multica= st address is set on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 multi_addr: The multicast addres= s to be added or removed from the filter.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add: If :data:'True', ad= d the specified multicast address to the port filter.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 If :data:'Fals= e', remove the specified multicast address from the port filter.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:'True',= assert that the 'mcast_addr' operations was successful.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 If :data:'Fals= e', execute the 'mcast_addr' operation and skip the assertion.<= br> -
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If either the 'add' or 'remove' operations fails.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 mcast_cmd =3D "add" if add else &quo= t;remove"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"mcast_addr= {mcast_cmd} {port_id} {multi_addr}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Bad arguments" in output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Invalid= arguments provided to mcast_addr")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("Invalid argument provided")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "Invalid mult= icast_addr" in output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or f"multicas= t address {'already' if add else 'not'} filtered by port&qu= ot; in output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to {mcast_cmd} {multi_addr} on port {port_id}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to {mcast_cmd} {multi_addr} on port {port_id} \n{output}" -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def show_port_stats_all(self) -> Tuple[list[TestPmdPortSt= ats], str]:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the statistics of al= l the ports.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Tuple[str, list[TestPmdPortStats= ]]: A tuple where the first element is the stats of all
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ports as `TestPmdPortStats` and = second is the raw testpmd output that was collected
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 from the sent command.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command("show port s= tats all")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Sample output of the "all" command= looks like:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0########### NIC statistics for p= ort 0 ###########
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0values...
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0################################= #################
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0########### NIC statistics for p= ort 1 ###########
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0values...
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #=C2=A0 =C2=A0################################= #################
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 #
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 iter =3D re.finditer(r"(^=C2=A0 #*.+#*$[^= #]+)^=C2=A0 #*\r$", output, re.MULTILINE)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return ([TestPmdPortStats.parse(block.group(1)= ) for block in iter], output)
-
-=C2=A0 =C2=A0 def show_port_stats(self, port_id: int) -> TestPmdPortSta= ts:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the given port stati= stics.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port ID to gather i= nformation for.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `port_id` is invalid.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdPortStats: An instance of= `TestPmdPortStats` containing the given port's stats.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"show port = stats {port_id}", skip_first_line=3DTrue)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if output.startswith("Invalid port")= :
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError("invalid port given")
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return TestPmdPortStats.parse(output)
-
-=C2=A0 =C2=A0 def set_multicast_all(self, on: bool, verify: bool =3D True)= -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Turns multicast mode on/off = for the specified port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 on: If :data:`True`, turns multi= cast mode on, otherwise turns off.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` an addit= ional command will be sent to verify
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 that multicast mod= e is properly set. Defaults to :data:`True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and multicast
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode is not proper= ly set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 multicast_cmd_output =3D self.send_command(f&q= uot;set allmulti all {'on' if on else 'off'}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_stats =3D self.show_port_in= fo_all()
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if on ^ all(stats.is_allmulticas= t_mode_enabled for stats in port_stats):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to set multicast mode on all ports.: \n{multicast_cmd_output}&quo= t;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &quo= t;Testpmd failed to set multicast mode on all ports."
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def csum_set_hw(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, layers: ChecksumOffloadOptions, port_id:= int, verify: bool =3D True
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Enables hardware checksum of= floading on the specified layer.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 layers: The layer/layers that ch= ecksum offloading should be enabled on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port number to enab= le checksum offloading on, should be within 0-32.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the command will be scanned in an attempt to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify that checks= um offloading was enabled on the port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If checksum offload is not enabled successfully.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for name, offload in ChecksumOffloadOptions.__= members__.items():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if offload in layers:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name =3D name.repl= ace("_", "-")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 csum_output =3D se= lf.send_command(f"csum set {name} hw {port_id}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 "Bad arguments" in csum_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 or f"Please stop port {port_id} first" in csum_output<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 or f"checksum offload is not supported by port {port_id}&qu= ot; in csum_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ): -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 self._logger.debug(f"Csum set hw error:\n{csum_output}"= ;)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 raise InteractiveCommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 f"Failed to set csum hw {name} mode on port {= port_id}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 success =3D False<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if f"{name} c= hecksum offload is hw" in csum_output.lower():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 succ= ess =3D True
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if not success and= verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self= ._logger.debug(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Failed to set csum hw mode on port {port_id}:\n{csum_outp= ut}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rais= e InteractiveCommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"""Failed to set csum hw mode on port
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{port_id}:\n{= csum_output}"""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) -
-=C2=A0 =C2=A0 def flow_create(self, flow_rule: FlowRule, port_id: int) -&g= t; int:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Creates a flow rule in the t= estpmd session.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 This command is implicitly verified as needed = to return the created flow rule id.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_rule: :class:`FlowRule` obj= ect used for creating testpmd flow rule.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: Integer representing th= e port to use.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If flow rule is invalid.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Id of created flow rule.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_output =3D self.send_command(f"flow = create {port_id} {flow_rule}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 match =3D re.search(r"#(\d+)", flow_= output)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if match is not None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 match_str =3D match.group(1)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_id =3D int(match_str)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return flow_id
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug(f"Failed= to create flow rule:\n{flow_output}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError(f"Failed to create flow rule:\n{flow_output}")
-
-=C2=A0 =C2=A0 def flow_validate(self, flow_rule: FlowRule, port_id: int) -= > bool:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Validates a flow rule in the= testpmd session.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_rule: :class:`FlowRule` obj= ect used for validating testpmd flow rule.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: Integer representing th= e port to use.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Boolean representing whether rul= e is valid or not.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_output =3D self.send_command(f"flow = validate {port_id} {flow_rule}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Flow rule validated" in flow_out= put:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return True
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return False
-
-=C2=A0 =C2=A0 def flow_delete(self, flow_id: int, port_id: int, verify: bo= ol =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Deletes the specified flow r= ule from the testpmd session.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_id: ID of the flow to remov= e.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: Integer representing th= e port to use.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 to ensure the flow= rule was deleted successfully.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If flow rule is invalid.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_output =3D self.send_command(f"flow = destroy {port_id} rule {flow_id}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "destroyed" not in = flow_output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to delete flow rule:\n{flow_output}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to delete flow rule:\n{flow_output}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 @requires_started_ports
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def set_port_mtu(self, port_id: int, mtu: int, verify: bool = =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Change the MTU of a port usi= ng testpmd.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Some PMDs require that the port be stopped bef= ore changing the MTU, and it does no harm to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 stop the port before configuring in cases wher= e it isn't required, so ports are stopped
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 prior to changing their MTU. On the other hand= , some PMDs require that the port had already
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 been started once since testpmd startup. There= fore, ports are also started before stopping
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 them to ensure this has happened.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port to adjus= t the MTU on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mtu: Desired value for the MTU t= o be set to.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If `verify` is :data:`Tr= ue` then the output will be scanned in an attempt to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify that the mt= u was properly set on the port. Defaults to :data:`True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the MTU was not
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 properly updated o= n the port matching `port_id`.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 set_mtu_output =3D self.send_command(f"po= rt config mtu {port_id} {mtu}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify and (f"MTU: {mtu}" not in = self.send_command(f"show port info {port_id}")):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"Failed to s= et mtu to {mtu} on port {port_id}. Output was:\n{set_mtu_output}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"Test pmd fa= iled to update mtu of port {port_id} to {mtu}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def set_port_mtu_all(self, mtu: int, verify: bool =3D True) = -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Change the MTU of all ports = using testpmd.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Runs :meth:`set_port_mtu` for every port that = testpmd is aware of.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mtu: Desired value for the MTU t= o be set to.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: Whether to verify that s= etting the MTU on each port was successful or not.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Defaults to :data:= `True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the MTU was not
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 properly updated o= n at least one port.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for port in self.ports:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.set_port_mtu(port.id, mtu, verify)=
-
-=C2=A0 =C2=A0 @staticmethod
-=C2=A0 =C2=A0 def extract_verbose_output(output: str) -> list[TestPmdVe= rbosePacket]:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Extract the verbose informat= ion present in given testpmd output.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 This method extracts sections of verbose outpu= t that begin with the line
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 "port X/queue Y: sent/received Z packets&= quot; and end with the ol_flags of a packet.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 output: Testpmd output that cont= ains verbose information
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 List of parsed packet informatio= n gathered from verbose information in `output`.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 out: list[TestPmdVerbosePacket] =3D []
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 prev_header: str =3D ""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 iter =3D re.finditer(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"(?P<HEADER>(?:port = \d+/queue \d+: (?:received|sent) \d+ packets)?)\s*"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r"(?P<PACKET>src=3D[\= w\s=3D:-]+?ol_flags: [\w ]+)",
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 output,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for match in iter:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if match.group("HEADER"= ;):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 prev_header =3D ma= tch.group("HEADER")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 out.append(TestPmdVerbosePacket.= parse(f"{prev_header}\n{match.group('PACKET')}"))
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return out
-
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def set_vlan_filter(self, port: int, enable: bool, verify: b= ool =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set vlan filter on.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port number to enable = VLAN filter on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: Enable the filter on `po= rt` if :data:`True`, otherwise disable it.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command and show port info
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is scanned to veri= fy that vlan filtering was set successfully.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the filter
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to update. -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 filter_cmd_output =3D self.send_command(f"= ;vlan set filter {'on' if enable else 'off'} {port}")<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan_settings =3D self.show_port= _info(port_id=3Dport).vlan_offload
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if enable ^ (vlan_settings is no= t None and VLANOffloadFlag.FILTER in vlan_settings):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;""Failed to {"enable" if enable else "disable&q= uot;}
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0filter on port {port}: = \n{filter_cmd_output}"""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;""Failed to {"enable" if enable else "disable&q= uot;}
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 filt= er on port {port}"""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def set_mac_address(self, port: int, mac_address: str, verif= y: bool =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set port's MAC address.<= br> -
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The number of the requeste= d port.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mac_address: The MAC address to = set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned to verify that
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the mac address is= set in the specified port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the command
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to execute.<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"mac_addr s= et {port} {mac_address}", skip_first_line=3DTrue)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if output.strip():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set MAC address {mac_address} on port {port}." -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set MAC address {mac_address} on port {port}." -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def set_flow_control(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port: int, flow_ctrl: TestPmdPortFlowCtr= l, verify: bool =3D True
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set the given `port`'s f= low control.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The number of the requeste= d port.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flow_ctrl: The requested flow co= ntrol parameters.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned to verify that
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the flow control i= n the specified port is set.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the command
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to execute.<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"set flow_c= trl {flow_ctrl} {port}", skip_first_line=3DTrue)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if output.strip():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Testpmd failed to set the {flow_ctrl} in port {port}.")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set the {flow_ctrl} in port {port}."
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def show_port_flow_info(self, port: int) -> TestPmdPortFl= owCtrl | None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Show port info flow.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The number of the requeste= d port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The current port flow control pa= rameters if supported, otherwise :data:`None`.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(f"show port = {port} flow_ctrl")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Flow control infos" in output: -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return TestPmdPortFlowCtrl.parse= (output)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return None
-
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def rx_vlan(self, vlan: int, port: int, add: bool, verify: b= ool =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add specified vlan tag to th= e filter list on a port. Requires vlan filter to be on.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan: The vlan tag to add, shoul= d be within 1-1005.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port number to add the= tag on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add: Adds the tag if :data:`True= `, otherwise removes the tag.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned to verify that
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the vlan tag was a= dded to the filter list on the specified port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the tag
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is not added.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 rx_cmd_output =3D self.send_command(f"rx_= vlan {'add' if add else 'rm'} {vlan} {port}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "VLAN-filteri= ng disabled" in rx_cmd_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or "Invalid v= lan_id" in rx_cmd_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or "Bad argum= ents" in rx_cmd_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;""Failed to {"add" if add else "remove"} t= ag {vlan}
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port= {port}: \n{rx_cmd_output}"""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to {'add' if add else 'remove'} tag {vlan= } on port {port}."
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def set_vlan_strip(self, port: int, enable: bool, verify: bo= ol =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Enable or disable vlan strip= ping on the specified port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port number to use. -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: If :data:`True`, will tu= rn vlan stripping on, otherwise will turn off.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command and show port info
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is scanned to veri= fy that vlan stripping was enabled on the specified port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and stripping
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fails to update. -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 strip_cmd_output =3D self.send_command(f"= vlan set strip {'on' if enable else 'off'} {port}") -=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan_settings =3D self.show_port= _info(port_id=3Dport).vlan_offload
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if enable ^ (vlan_settings is no= t None and VLANOffloadFlag.STRIP in vlan_settings):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;""Failed to set strip {"on" if enable else "off= "}
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port= {port}: \n{strip_cmd_output}"""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set strip {'on' if enable else 'off'} = port {port}."
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def tx_vlan_set(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port: int, enable: bool, vlan: int | Non= e =3D None, verify: bool =3D True
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set hardware insertion of vl= an tags in packets sent on a port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port number to use. -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: Sets vlan tag insertion = if :data:`True`, and resets if :data:`False`.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan: The vlan tag to insert if = enable is :data:`True`.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is scanned to verify that
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vlan insertion was= enabled on the specified port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the insertion
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 tag is not set. -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if enable:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 tx_vlan_cmd_output =3D self.send= _command(f"tx_vlan set {port} {vlan}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &quo= t;Please stop port" in tx_vlan_cmd_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or &= quot;Invalid vlan_id" in tx_vlan_cmd_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 or &= quot;Invalid port" in tx_vlan_cmd_output
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self= ._logger.debug(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Failed to set vlan tag {vlan} on port {port}:\n{tx_vlan_c= md_output}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rais= e InteractiveCommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Testpmd failed to set vlan insertion tag {vlan} on port {= port}."
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) -=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 tx_vlan_cmd_output =3D self.send= _command(f"tx_vlan reset {port}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Please st= op port" in tx_vlan_cmd_output or "Invalid port" in tx_vlan_= cmd_output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self= ._logger.debug(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Failed to reset vlan insertion on port {port}: \n{tx_vlan= _cmd_output}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rais= e InteractiveCommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 f"Testpmd failed to reset vlan insertion on port {port}.&qu= ot;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) -
-=C2=A0 =C2=A0 def set_promisc(self, port: int, enable: bool, verify: bool = =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Enable or disable promiscuou= s mode for the specified port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: Port number to use.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: If :data:`True`, turn pr= omiscuous mode on, otherwise turn off.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` an addit= ional command will be sent to verify that
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 promiscuous mode i= s properly set. Defaults to :data:`True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and promiscuous mode
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is not correctly s= et.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 promisc_cmd_output =3D self.send_command(f&quo= t;set promisc {port} {'on' if enable else 'off'}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stats =3D self.show_port_info(po= rt_id=3Dport)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if enable ^ stats.is_promiscuous= _mode_enabled:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to set promiscuous mode on port {port}: \n{promisc_cmd_output}&qu= ot;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set promiscuous mode on port {port}."
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def set_verbose(self, level: int, verify: bool =3D True) -&g= t; None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set debug verbosity level. -
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 level: 0 - silent except for err= or
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 1 - fully verbose = except for Tx packets
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 2 - fully verbose = except for Rx packets
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 >2 - fully verb= ose
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the comm= and output will be scanned to verify that verbose level
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is properly set. D= efaults to :data:`True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and verbose level
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is not correctly set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 verbose_cmd_output =3D self.send_command(f&quo= t;set verbose {level}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Change verbose level&qu= ot; not in verbose_cmd_output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to set verbose level to {level}: \n{verbose_cmd_output}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Testpmd failed to set verbose level to {level}."
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def rx_vxlan(self, vxlan_id: int, port_id: int, enable: bool= , verify: bool =3D True) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add or remove vxlan id to/fr= om filter list.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vxlan_id: VXLAN ID to add to por= t filter list.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port to modif= y VXLAN filter of.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: If :data:`True`, adds sp= ecified VXLAN ID, otherwise removes it.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, the out= put of the command is checked to verify
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the VXLAN ID was s= uccessfully added/removed from the port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and VXLAN ID
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is not successfull= y added or removed.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 action =3D "add" if enable else &quo= t;rm"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 vxlan_output =3D self.send_command(f"rx_v= xlan_port {action} {vxlan_id} {port_id}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "udp tunneling add error= " in vxlan_output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (f"Failed to set VXLAN:\n{vxlan_output}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(f"Failed to set VXLAN:\n{vxlan_output}")
-
-=C2=A0 =C2=A0 def clear_port_stats(self, port_id: int, verify: bool =3D Tr= ue) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Clear statistics of a given = port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port to clear= the statistics on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the command will be scanned to verify that it was
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 successful, otherw= ise failures will be ignored. Defaults to :data:`True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and testpmd fails to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clear the statisti= cs of the given port.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 clear_output =3D self.send_command(f"clea= r port stats {port_id}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify and f"NIC statistics for port {= port_id} cleared" not in clear_output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveCommandExecutio= nError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f"Test pmd fa= iled to set clear forwarding stats on port {port_id}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def clear_port_stats_all(self, verify: bool =3D True) -> = None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Clear the statistics of all = ports that testpmd is aware of.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` the outp= ut of the command will be scanned to verify that all
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ports had their st= atistics cleared, otherwise failures will be ignored. Defaults to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 :data:`True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and testpmd fails to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clear the statisti= cs of any of its ports.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 clear_output =3D self.send_command("clear= port stats all")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if type(self._app_params.port_nu= ma_config) is list:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for port_id in ran= ge(len(self._app_params.port_numa_config)):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if f= "NIC statistics for port {port_id} cleared" not in clear_output:<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 raise InteractiveCommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 f"Test pmd failed to set clear forwarding sta= ts on port {port_id}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 )
-
-=C2=A0 =C2=A0 @only_active
-=C2=A0 =C2=A0 def close(self) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Overrides :meth:`~.interacti= ve_shell.close`."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.stop()
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command("quit", "Bye.= ..")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return super().close()
-
-=C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =3D=3D=3D=3D=3D=3D Capability retrieval methods =3D=3D=3D=3D= =3D=3D
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 def get_capabilities_rx_offload(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get all rx offload capabilit= ies and divide them into supported and unsupported.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Getting rx offload ca= pabilities.")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show port {self.ports[0].id= } rx_offload capabilities"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 rx_offload_capabilities_out =3D self.send_comm= and(command)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 rx_offload_capabilities =3D RxOffloadCapabilit= ies.parse(rx_offload_capabilities_out)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._update_capabilities_from_flag(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RxOffloadCapability,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rx_offload_capabilities.per_port= | rx_offload_capabilities.per_queue,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def get_port_queue_info(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, queue_id: int, is_rx_queue= : bool
-=C2=A0 =C2=A0 ) -> TestPmdQueueInfo:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the current state of= the specified queue."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show {'rxq' if is_r= x_queue else 'txq'} info {port_id} {queue_id}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_info =3D TestPmdQueueInfo.parse(self.sen= d_command(command))
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return queue_info
-
-=C2=A0 =C2=A0 def setup_port_queue(self, port_id: int, queue_id: int, is_r= x_queue: bool) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Setup a given queue on a por= t.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 This functionality cannot be verified because = the setup action only takes effect when the
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue is started.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port where th= e queue resides.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: ID of the queue to set= up.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Type of queue to se= tup. If :data:`True` an RX queue will be setup,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 otherwise a TX que= ue will be setup.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port {port_id} {'= rxq' if is_rx_queue else 'txq'} {queue_id} setup")
-
-=C2=A0 =C2=A0 def stop_port_queue(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, queue_id: int, is_rx_queue= : bool, verify: bool =3D True
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Stops a given queue on a por= t.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port that the= queue belongs to.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: ID of the queue to sto= p.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Type of queue to st= op. If :data:`True` an RX queue will be stopped,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 otherwise a TX que= ue will be stopped.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` an addit= ional command will be sent to verify the queue stopped.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Defaults to :data:= `True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the queue fails to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stop.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_type =3D "rxq" if is_rx_queue e= lse "txq"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 stop_cmd_output =3D self.send_command(f"p= ort {port_id} {port_type} {queue_id} stop")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_started =3D self.get_port_= queue_info(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id, queue_id,= is_rx_queue
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ).is_queue_started
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if queue_started:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_outp= ut}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Test pmd failed to stop {port_type} {queue_id} on port {port_id}" -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def start_port_queue(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, queue_id: int, is_rx_queue= : bool, verify: bool =3D True
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Starts a given queue on a po= rt.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 First sets up the port queue, then starts it.<= br> -
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: ID of the port that the= queue belongs to.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: ID of the queue to sta= rt.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Type of queue to st= art. If :data:`True` an RX queue will be started,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 otherwise a TX que= ue will be started.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: if :data:`True` an addit= ional command will be sent to verify that the queue was
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 started. Defaults = to :data:`True`.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and the queue fails to
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 start.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_type =3D "rxq" if is_rx_queue e= lse "txq"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.setup_port_queue(port_id, queue_id, is_rx= _queue)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 start_cmd_output =3D self.send_command(f"= port {port_id} {port_type} {queue_id} start")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_started =3D self.get_port_= queue_info(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id, queue_id,= is_rx_queue
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ).is_queue_started
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if not queue_started:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_ou= tput}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Test pmd failed to start {port_type} {queue_id} on port {port_id}"<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def get_queue_ring_size(self, port_id: int, queue_id: int, i= s_rx_queue: bool) -> int:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Returns the current size of = the ring on the specified queue."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show {'rxq' if is_r= x_queue else 'txq'} info {port_id} {queue_id}"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_info =3D TestPmdQueueInfo.parse(self.sen= d_command(command))
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return queue_info.ring_size
-
-=C2=A0 =C2=A0 def set_queue_ring_size(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: int,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: int,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 size: int,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: bool,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: bool =3D True,
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Update the ring size of an R= x/Tx queue on a given port.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Queue is setup after setting the ring size so = that the queue info reflects this change and
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 it can be verified.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port that the queue= resides on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: The ID of the queue on= the port.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size: The size to update the rin= g size to.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Whether to modify a= n RX or TX queue. If :data:`True` an RX queue will be
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 updated, otherwise= a TX queue will be updated.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True` an addit= ional command will be sent to check the ring size of
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the queue in an at= tempt to validate that the size was changes properly.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 InteractiveCommandExecutionError= : If `verify` is :data:`True` and there is a failure
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 when updating ring= size.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_type =3D "rxq" if is_rx_queue = else "txq"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port config {port_id}= {queue_type} {queue_id} ring_size {size}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.setup_port_queue(port_id, queue_id, is_rx= _queue)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if verify:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 curr_ring_size =3D self.get_queu= e_ring_size(port_id, queue_id, is_rx_queue)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if curr_ring_size !=3D size:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug= (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed up update ring size of queue {queue_id} on port {port_id}. Curren= t"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot; ring size is {curr_ring_size}."
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise InteractiveC= ommandExecutionError(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 f&qu= ot;Failed to update ring size of queue {queue_id} on port {port_id}" -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 @requires_stopped_ports
-=C2=A0 =C2=A0 def set_queue_deferred_start(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self, port_id: int, queue_id: int, is_rx_queue= : bool, on: bool
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Set the deferred start attri= bute of the specified queue on/off.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port_id: The port that the queue= resides on.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_id: The ID of the queue on= the port.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is_rx_queue: Whether to modify a= n RX or TX queue. If :data:`True` an RX queue will be
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 updated, otherwise= a TX queue will be updated.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 on: Whether to set deferred star= t mode on or off. If :data:`True` deferred start will
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 be turned on, othe= rwise it will be turned off.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 queue_type =3D "rxq" if is_rx_queue = else "txq"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 action =3D "on" if on else "off= "
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(f"port {port_id} {queue= _type} {queue_id} deferred_start {action}")
-
-=C2=A0 =C2=A0 def _update_capabilities_from_flag(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 flag_class: type[Flag],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_flags: Flag,
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Divide all flags from `flag_= class` into supported and unsupported."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for flag in flag_class:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if flag in supported_flags:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabili= ties.add(NicCapability[str(flag.name)])
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabi= lities.add(NicCapability[str(flag.name)])
-
-=C2=A0 =C2=A0 @requires_started_ports
-=C2=A0 =C2=A0 def get_capabilities_rxq_info(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get all rxq capabilities and= divide them into supported and unsupported.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Getting rxq capabilit= ies.")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show rxq info {self.ports[0= ].id} 0"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 rxq_info =3D TestPmdRxqInfo.parse(self.send_co= mmand(command))
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if rxq_info.scattered_packets:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities.add(NicCa= pability.SCATTERED_RX_ENABLED)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities.add(Nic= Capability.SCATTERED_RX_ENABLED)
-
-=C2=A0 =C2=A0 def get_capabilities_show_port_info(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get all capabilities from sh= ow port info and divide them into supported and unsupported.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._update_capabilities_from_flag(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 DeviceCapabilitiesFlag,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.ports[0].device_capabilitie= s,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def get_capabilities_mcast_filtering(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get multicast filtering capa= bility from mcast_addr add and check for testpmd error code.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Getting mcast filter = capabilities.")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"mcast_addr add {self.ports[= 0].id} 01:00:5E:00:00:00"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(command)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "diag=3D-95" in output:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities.add(Nic= Capability.MCAST_FILTERING)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities.add(NicCa= pability.MCAST_FILTERING)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D str.replace(command,= "add", "remove", 1)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_command(command)
-
-=C2=A0 =C2=A0 def get_capabilities_flow_ctrl(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get flow control capability = and check for testpmd failure.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self._logger.debug("Getting flow ctrl cap= abilities.")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 command =3D f"show port {self.ports[0].id= } flow_ctrl"
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 output =3D self.send_command(command)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "Flow control infos" in output: -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities.add(NicCa= pability.FLOW_CTRL)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities.add(Nic= Capability.FLOW_CTRL)
-
-=C2=A0 =C2=A0 def get_capabilities_physical_function(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet["NicCa= pability"],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet["Nic= Capability"],
-=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Store capability representin= g a physical function test run.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: Supporte= d capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: Unsupp= orted capabilities will be added to this set.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 ctx =3D get_ctx()
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if ctx.topology.vf_ports =3D=3D []:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities.add(NicCa= pability.PHYSICAL_FUNCTION)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities.add(Nic= Capability.PHYSICAL_FUNCTION)
-
-
-class NicCapability(NoAliasEnum):
-=C2=A0 =C2=A0 """A mapping between capability names and the= associated :class:`TestPmdShell` methods.
-
-=C2=A0 =C2=A0 The :class:`TestPmdShell` capability checking method execute= s the command that checks
-=C2=A0 =C2=A0 whether the capability is supported.
-=C2=A0 =C2=A0 A decorator may optionally be added to the method that will = add and remove configuration
-=C2=A0 =C2=A0 that's necessary to retrieve the capability support stat= us.
-=C2=A0 =C2=A0 The Enum members may be assigned the method itself or a tupl= e of
-=C2=A0 =C2=A0 (capability_checking_method, decorator_function).
-
-=C2=A0 =C2=A0 The signature of each :class:`TestPmdShell` capability check= ing method must be::
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 fn(self, supported_capabilities: MutableSet, u= nsupported_capabilities: MutableSet) -> None
-
-=C2=A0 =C2=A0 The capability checking method must get whether a capability= is supported or not
-=C2=A0 =C2=A0 from a testpmd command. If multiple capabilities can be obta= ined from a testpmd command,
-=C2=A0 =C2=A0 each should be obtained in the method. These capabilities sh= ould then
-=C2=A0 =C2=A0 be added to `supported_capabilities` or `unsupported_capabil= ities` based on their support.
-
-=C2=A0 =C2=A0 The two dictionaries are shared across all capability discov= ery function calls in a given
-=C2=A0 =C2=A0 test run so that we don't call the same function multipl= e times. For example, when we find
-=C2=A0 =C2=A0 :attr:`SCATTERED_RX_ENABLED` in :meth:`TestPmdShell.get_capa= bilities_rxq_info`,
-=C2=A0 =C2=A0 we don't go looking for it again if a different test cas= e also needs it.
-=C2=A0 =C2=A0 """
-
-=C2=A0 =C2=A0 #: Scattered packets Rx enabled
-=C2=A0 =C2=A0 SCATTERED_RX_ENABLED: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rxq_info,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 add_remove_mtu(9000),
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #:
-=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_STRIP: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports L3 checksum offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_IPV4_CKSUM: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_UDP_CKSUM: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_TCP_CKSUM: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports Large Receive Offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_TCP_LRO: TestPmdShellNicCapability =3D (TestPmdSh= ell.get_capabilities_rx_offload, None)
-=C2=A0 =C2=A0 #: Device supports QinQ (queue in queue) offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_QINQ_STRIP: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports inner packet L3 checksum.
-=C2=A0 =C2=A0 RX_OFFLOAD_OUTER_IPV4_CKSUM: TestPmdShellNicCapability =3D (=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports MACsec.
-=C2=A0 =C2=A0 RX_OFFLOAD_MACSEC_STRIP: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports filtering of a VLAN Tag identifier.
-=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_FILTER: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports VLAN offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_VLAN_EXTEND: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports receiving segmented mbufs.
-=C2=A0 =C2=A0 RX_OFFLOAD_SCATTER: TestPmdShellNicCapability =3D (TestPmdSh= ell.get_capabilities_rx_offload, None)
-=C2=A0 =C2=A0 #: Device supports Timestamp.
-=C2=A0 =C2=A0 RX_OFFLOAD_TIMESTAMP: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports crypto processing while packet is receive= d in NIC.
-=C2=A0 =C2=A0 RX_OFFLOAD_SECURITY: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports CRC stripping.
-=C2=A0 =C2=A0 RX_OFFLOAD_KEEP_CRC: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports L4 checksum offload.
-=C2=A0 =C2=A0 RX_OFFLOAD_SCTP_CKSUM: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports inner packet L4 checksum.
-=C2=A0 =C2=A0 RX_OFFLOAD_OUTER_UDP_CKSUM: TestPmdShellNicCapability =3D (<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports RSS hashing.
-=C2=A0 =C2=A0 RX_OFFLOAD_RSS_HASH: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports scatter Rx packets to segmented mbufs. -=C2=A0 =C2=A0 RX_OFFLOAD_BUFFER_SPLIT: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports all checksum capabilities.
-=C2=A0 =C2=A0 RX_OFFLOAD_CHECKSUM: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_rx_offload,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports all VLAN capabilities.
-=C2=A0 =C2=A0 RX_OFFLOAD_VLAN: TestPmdShellNicCapability =3D (TestPmdShell= .get_capabilities_rx_offload, None)
-=C2=A0 =C2=A0 #: Device supports Rx queue setup after device started.
-=C2=A0 =C2=A0 RUNTIME_RX_QUEUE_SETUP: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_show_port_info,<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports Tx queue setup after device started.
-=C2=A0 =C2=A0 RUNTIME_TX_QUEUE_SETUP: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_show_port_info,<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports shared Rx queue among ports within Rx dom= ain and switch domain.
-=C2=A0 =C2=A0 RXQ_SHARE: TestPmdShellNicCapability =3D (TestPmdShell.get_c= apabilities_show_port_info, None)
-=C2=A0 =C2=A0 #: Device supports keeping flow rules across restart.
-=C2=A0 =C2=A0 FLOW_RULE_KEEP: TestPmdShellNicCapability =3D (TestPmdShell.= get_capabilities_show_port_info, None)
-=C2=A0 =C2=A0 #: Device supports keeping shared flow objects across restar= t.
-=C2=A0 =C2=A0 FLOW_SHARED_OBJECT_KEEP: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_show_port_info,<= br> -=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports multicast address filtering.
-=C2=A0 =C2=A0 MCAST_FILTERING: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_mcast_filtering,=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-=C2=A0 =C2=A0 #: Device supports flow ctrl.
-=C2=A0 =C2=A0 FLOW_CTRL: TestPmdShellNicCapability =3D (TestPmdShell.get_c= apabilities_flow_ctrl, None)
-=C2=A0 =C2=A0 #: Device is running on a physical function.
-=C2=A0 =C2=A0 PHYSICAL_FUNCTION: TestPmdShellNicCapability =3D (
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 TestPmdShell.get_capabilities_physical_functio= n,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 None,
-=C2=A0 =C2=A0 )
-
-=C2=A0 =C2=A0 def __call__(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd_shell: TestPmdShell,
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: MutableSet[Self],
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: MutableSet[Self], -=C2=A0 =C2=A0 ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Execute the associated capab= ility retrieval function.
-
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 testpmd_shell: :class:`TestPmdSh= ell` object to which the function will be bound to.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 supported_capabilities: The dict= ionary storing the supported capabilities
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 of a given test ru= n.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsupported_capabilities: The di= ctionary storing the unsupported capabilities
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 of a given test ru= n.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.value(testpmd_shell, supported_capabiliti= es, unsupported_capabilities)
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/test= bed_model/capability.py
index f895b22bb3..a4a8d9b7b4 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -26,9 +26,9 @@
=C2=A0 =C2=A0 =C2=A0.. code:: python

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0from framework.test_suite import TestSuit= e, func_test
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 from framework.testbed_model.capability import= TopologyType, requires
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 from framework.testbed_model.capability import= LinkTopology, requires
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0# The whole test suite (each test case wi= thin) doesn't require any links.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 @requires(topology_type=3DTopologyType.no_link= )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 @requires_link_topology(LinkTopology.NO_LINK)<= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0@func_test
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0class TestHelloWorld(TestSuite):
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0def hello_world_single_core= (self):
@@ -41,7 +41,7 @@ def hello_world_single_core(self):
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0class TestPmdBufferScatter(TestSuite): =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0# only the test case requir= es the SCATTERED_RX_ENABLED capability
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0# other test cases may not = require it
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 @requires(NicCapability.SCATTERE= D_RX_ENABLED)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 @requires_nic_capability(NicCapa= bility.SCATTERED_RX_ENABLED)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0@func_test
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0def test_scatter_mbuf_2048(= self):
=C2=A0"""
@@ -50,27 +50,38 @@ def test_scatter_mbuf_2048(self):
=C2=A0from abc import ABC, abstractmethod
=C2=A0from collections.abc import MutableSet
=C2=A0from dataclasses import dataclass
-from typing import TYPE_CHECKING, Callable, ClassVar, Protocol
+from typing import (
+=C2=A0 =C2=A0 TYPE_CHECKING,
+=C2=A0 =C2=A0 Any,
+=C2=A0 =C2=A0 Callable,
+=C2=A0 =C2=A0 ClassVar,
+=C2=A0 =C2=A0 Concatenate,
+=C2=A0 =C2=A0 ParamSpec,
+=C2=A0 =C2=A0 Protocol,
+=C2=A0 =C2=A0 TypeAlias,
+)

=C2=A0from typing_extensions import Self

+from api.capabilities import LinkTopology, NicCapability
=C2=A0from framework.exception import ConfigurationError, InternalError, Sk= ippedTestException
=C2=A0from framework.logger import get_dts_logger
-from framework.remote_session.testpmd_shell import (
-=C2=A0 =C2=A0 NicCapability,
-=C2=A0 =C2=A0 TestPmdShell,
-=C2=A0 =C2=A0 TestPmdShellCapabilityMethod,
-=C2=A0 =C2=A0 TestPmdShellDecorator,
-=C2=A0 =C2=A0 TestPmdShellMethod,
-)
=C2=A0from framework.testbed_model.node import Node
=C2=A0from framework.testbed_model.port import DriverKind
-
-from .topology import Topology, TopologyType
+from framework.testbed_model.topology import Topology

=C2=A0if TYPE_CHECKING:
+=C2=A0 =C2=A0 from api.testpmd import TestPmd
=C2=A0 =C2=A0 =C2=A0from framework.test_suite import TestCase

+P =3D ParamSpec("P")
+TestPmdMethod =3D Callable[Concatenate["TestPmd", P], Any]
+TestPmdCapabilityMethod: TypeAlias =3D Callable[
+=C2=A0 =C2=A0 ["TestPmd", MutableSet["NicCapability"],= MutableSet["NicCapability"]], None
+]
+TestPmdDecorator: TypeAlias =3D Callable[[TestPmdMethod], TestPmdMethod] +TestPmdNicCapability =3D tuple[TestPmdCapabilityMethod, TestPmdDecorator |= None]
+

=C2=A0class Capability(ABC):
=C2=A0 =C2=A0 =C2=A0"""The base class for various capabiliti= es.
@@ -153,7 +164,7 @@ def __hash__(self) -> int:

=C2=A0@dataclass
=C2=A0class DecoratedNicCapability(Capability):
-=C2=A0 =C2=A0 """A wrapper around :class:`~framework.remote= _session.testpmd_shell.NicCapability`.
+=C2=A0 =C2=A0 """A wrapper around :class:`~api.testpmd.NicC= apability`.

=C2=A0 =C2=A0 =C2=A0New instances should be created with the :meth:`create_= unique` class method to ensure
=C2=A0 =C2=A0 =C2=A0there are no duplicate instances.
@@ -166,10 +177,69 @@ class DecoratedNicCapability(Capability):
=C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0nic_capability: NicCapability
-=C2=A0 =C2=A0 capability_fn: TestPmdShellCapabilityMethod
-=C2=A0 =C2=A0 capability_decorator: TestPmdShellDecorator | None
+=C2=A0 =C2=A0 capability_fn: TestPmdCapabilityMethod
+=C2=A0 =C2=A0 capability_decorator: TestPmdDecorator | None
=C2=A0 =C2=A0 =C2=A0_unique_capabilities: ClassVar[dict[NicCapability, Self= ]] =3D {}

+=C2=A0 =C2=A0 @classmethod
+=C2=A0 =C2=A0 def _get_nic_capability_check(cls) -> list[TestPmdNicCapa= bility]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """A mapping between capability= names and the associated :class:`TestPmd` methods.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The :class:`TestPmd` capability checking metho= d executes the command that checks
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 whether the capability is supported.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 A decorator may optionally be added to the met= hod that will add and remove configuration
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 that's necessary to retrieve the capabilit= y support status.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The Enum members may be assigned the method it= self or a tuple of
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 (capability_checking_method, decorator_functio= n).
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The signature of each :class:`TestPmd` capabil= ity checking method must be::
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fn(self, supported_capabilities:= MutableSet, unsupported_capabilities: MutableSet)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The capability checking method must get whethe= r a capability is supported or not
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 from a testpmd command. If multiple capabiliti= es can be obtained from a testpmd command,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 each should be obtained in the method. These c= apabilities should then
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 be added to `supported_capabilities` or `unsup= ported_capabilities` based on their support.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The two dictionaries are shared across all cap= ability discovery function calls in a given
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 test run so that we don't call the same fu= nction multiple times. For example, when we find
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 :attr:`SCATTERED_RX_ENABLED` in :meth:`TestPmd= .get_capabilities_rxq_info`,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 we don't go looking for it again if a diff= erent test case also needs it.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 from api.testpmd import TestPmd, _add_remove_m= tu
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return [
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rxq_in= fo, _add_remove_mtu(9000)),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_VLAN_STRIP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_IPV4_CKSUM
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_UDP_CKSUM
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_TCP_CKSUM
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_TCP_LRO
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_QINQ_STRIP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_OUTER_IPV4_CKSUM
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_MACSEC_STRIP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_VLAN_FILTER
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_VLAN_EXTEND
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_SCATTER
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_TIMESTAMP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_SECURITY
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_KEEP_CRC
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_SCTP_CKSUM
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_OUTER_UDP_CKSUM
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_RSS_HASH
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_BUFFER_SPLIT
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_CHECKSUM
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_rx_off= load, None),=C2=A0 # RX_OFFLOAD_VLAN
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_show_p= ort_info, None),=C2=A0 # RUNTIME_RX_QUEUE_SETUP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_show_p= ort_info, None),=C2=A0 # RUNTIME_TX_QUEUE_SETUP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_show_p= ort_info, None),=C2=A0 # RXQ_SHARE
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_show_p= ort_info, None),=C2=A0 # FLOW_RULE_KEEP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_show_p= ort_info, None),=C2=A0 # FLOW_SHARED_OBJECT_KEEP
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_mcast_= filtering, None),=C2=A0 # MCAST_FILTERING
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_flow_c= trl, None),=C2=A0 # FLOW_CTRL
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (TestPmd.get_capabilities_physic= al_function, None),=C2=A0 # PHYSICAL_FUNCTION
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ]
+
=C2=A0 =C2=A0 =C2=A0@classmethod
=C2=A0 =C2=A0 =C2=A0def get_unique(cls, nic_capability: NicCapability) ->= ; Self:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""Get the capability uniq= uely identified by `nic_capability`.
@@ -188,7 +258,7 @@ def get_unique(cls, nic_capability: NicCapability) ->= ; Self:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Returns:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0The capability uniquely ide= ntified by `nic_capability`.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 capability_fn, decorator_fn =3D nic_capability= .value
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 capability_fn, decorator_fn =3D cls._get_nic_c= apability_check()[nic_capability.value]

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if nic_capability not in cls._unique_capa= bilities:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cls._unique_capabilities[ni= c_capability] =3D cls(
@@ -207,9 +277,11 @@ def get_supported_capabilities(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Each capability is first checked whether = it's supported/unsupported
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0before executing its `capability_fn` so t= hat each capability is retrieved only once.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 from api.testpmd import TestPmd
+
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0supported_conditional_capabilities: set[&= quot;DecoratedNicCapability"] =3D set()
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0logger =3D get_dts_logger(f"{node.name}.{= cls.__name__}")
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if topology.type is topology.type.no_link:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if topology.type is topology.type.NO_LINK:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0logger.debug(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"No link= s available in the current topology, not getting NIC capabilities." =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0)
@@ -219,7 +291,7 @@ def get_supported_capabilities(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if cls.capabilities_to_check:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0capabilities_to_check_map = =3D cls._get_decorated_capabilities_map()
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 with TestPmdShell() as testpmd_s= hell:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 with TestPmd() as testpmd:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0for (
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0conditional_capability_fn,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0capabilities,
@@ -231,7 +303,7 @@ def get_supported_capabilities(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0if conditional_capability_fn:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0capability_fn =3D conditional_capability_fn(capability_fn)=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 capa= bility_fn(testpmd_shell)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 capa= bility_fn(testpmd)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0for capability in capabilities:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0if capability.nic_capability in supported_capabilities: =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0supported_conditional_capabilities.add(capab= ility)
@@ -242,8 +314,8 @@ def get_supported_capabilities(
=C2=A0 =C2=A0 =C2=A0@classmethod
=C2=A0 =C2=A0 =C2=A0def _get_decorated_capabilities_map(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cls,
-=C2=A0 =C2=A0 ) -> dict[TestPmdShellDecorator | None, set["Decorat= edNicCapability"]]:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 capabilities_map: dict[TestPmdShellDecorator |= None, set["DecoratedNicCapability"]] =3D {}
+=C2=A0 =C2=A0 ) -> dict[TestPmdDecorator | None, set["DecoratedNic= Capability"]]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 capabilities_map: dict[TestPmdDecorator | None= , set["DecoratedNicCapability"]] =3D {}
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0for capability in cls.capabilities_to_che= ck:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if capability.capability_de= corator not in capabilities_map:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0capabilities_= map[capability.capability_decorator] =3D set()
@@ -257,12 +329,12 @@ def _reduce_capabilities(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0capabilities: set["DecoratedNicCapab= ility"],
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0supported_capabilities: MutableSet,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0unsupported_capabilities: MutableSet,
-=C2=A0 =C2=A0 ) -> TestPmdShellMethod:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 def reduced_fn(testpmd_shell: TestPmdShell) -&= gt; None:
+=C2=A0 =C2=A0 ) -> TestPmdMethod:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 def reduced_fn(testpmd: "TestPmd") -= > None:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0for capability in capabilit= ies:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if capability= not in supported_capabilities | unsupported_capabilities:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0capability.capability_fn(
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 testpmd_shell, supported_capabilities, unsupported_capabilities<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 testpmd, supported_capabilities, unsupported_capabilities
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0)

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return reduced_fn
@@ -278,11 +350,11 @@ def __repr__(self) -> str:

=C2=A0@dataclass
=C2=A0class TopologyCapability(Capability):
-=C2=A0 =C2=A0 """A wrapper around :class:`~.topology.Topolo= gyType`.
+=C2=A0 =C2=A0 """A wrapper around :class:`~.topology.LinkTo= pology`.

=C2=A0 =C2=A0 =C2=A0Each test case must be assigned a topology. It could be= done explicitly;
-=C2=A0 =C2=A0 the implicit default is given by :meth:`~.topology.TopologyT= ype.default`, which this class
-=C2=A0 =C2=A0 returns :attr:`~.topology.TopologyType.two_links`.
+=C2=A0 =C2=A0 the implicit default is given by :meth:`~.topology.LinkTopol= ogy.default`, which this class
+=C2=A0 =C2=A0 returns :attr:`~.topology.LinkTopology.TWO_LINKS`.

=C2=A0 =C2=A0 =C2=A0Test case topology may be set by setting the topology f= or the whole suite.
=C2=A0 =C2=A0 =C2=A0The priority in which topology is set is as follows: @@ -301,7 +373,7 @@ class TopologyCapability(Capability):
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0topology_type: The topology type that def= ines each instance.
=C2=A0 =C2=A0 =C2=A0"""

-=C2=A0 =C2=A0 topology_type: TopologyType
+=C2=A0 =C2=A0 topology_type: LinkTopology

=C2=A0 =C2=A0 =C2=A0_unique_capabilities: ClassVar[dict[str, Self]] =3D {}<= br>
@@ -310,7 +382,7 @@ def _preprocess_required(self, test_case_or_suite: type= ["TestProtocol"]) -> None
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0test_case_or_suite.topology_type =3D self=

=C2=A0 =C2=A0 =C2=A0@classmethod
-=C2=A0 =C2=A0 def get_unique(cls, topology_type: TopologyType) -> Self:=
+=C2=A0 =C2=A0 def get_unique(cls, topology_type: LinkTopology) -> Self:=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""Get the capability uniq= uely identified by `topology_type`.

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0This is a factory method that implements = a quasi-enum pattern.
@@ -338,7 +410,7 @@ def get_supported_capabilities(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""Overrides :meth:`~Capab= ility.get_supported_capabilities`."""
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0supported_capabilities =3D set()
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0topology_capability =3D cls.get_unique(to= pology.type)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 for topology_type in TopologyType:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for topology_type in LinkTopology:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0candidate_topology_type =3D= cls.get_unique(topology_type)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if candidate_topology_type = <=3D topology_capability:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0supported_cap= abilities.add(candidate_topology_type)
@@ -351,17 +423,17 @@ def set_required(self, test_case_or_suite: type["= ;TestProtocol"]) -> None:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0This means we have to modify test case to= pologies when processing the test suite topologies.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0At that point, the test case topologies h= ave been set by the :func:`requires` decorator.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0The test suite topology only affects the = test case topologies
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not :attr:`~.topology.TopologyType.default`= .
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not :attr:`~.topology.LinkTopology.default`= .

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Raises:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ConfigurationError: If the = topology type requested by the test case is more complex than
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0the test suit= e's.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if inspect.isclass(test_case_or_suite): -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.topology_type is not Top= ologyType.default():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if self.topology_type is not Lin= kTopology.default():
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self.add_to_r= equired(test_case_or_suite)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0for test_case= in test_case_or_suite.get_test_cases():
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if t= est_case.topology_type.topology_type is TopologyType.default():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if t= est_case.topology_type.topology_type is LinkTopology.default():
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0# test case topology has not been set, use the one set by = the test suite
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0self.add_to_required(test_case)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0elif test_case.topology_type > test_case_or_suite.topology_type:
@@ -440,7 +512,7 @@ class TestProtocol(Protocol):
=C2=A0 =C2=A0 =C2=A0#: The reason for skipping the test case or suite.
=C2=A0 =C2=A0 =C2=A0skip_reason: ClassVar[str] =3D ""
=C2=A0 =C2=A0 =C2=A0#: The topology type of the test case or suite.
-=C2=A0 =C2=A0 topology_type: ClassVar[TopologyCapability] =3D TopologyCapa= bility(TopologyType.default())
+=C2=A0 =C2=A0 topology_type: ClassVar[TopologyCapability] =3D TopologyCapa= bility(LinkTopology.default())
=C2=A0 =C2=A0 =C2=A0#: The capabilities the test case or suite requires in = order to be executed.
=C2=A0 =C2=A0 =C2=A0required_capabilities: ClassVar[set[Capability]] =3D se= t()
=C2=A0 =C2=A0 =C2=A0#: The SUT ports topology configuration of the test cas= e or suite.
@@ -481,7 +553,7 @@ def _decorator(func: type[TestProtocol]) -> type[Tes= tProtocol]:

=C2=A0def requires(
=C2=A0 =C2=A0 =C2=A0*nic_capabilities: NicCapability,
-=C2=A0 =C2=A0 topology_type: TopologyType =3D TopologyType.default(),
+=C2=A0 =C2=A0 topology_type: LinkTopology =3D LinkTopology.default(),
=C2=A0) -> Callable[[type[TestProtocol]], type[TestProtocol]]:
=C2=A0 =C2=A0 =C2=A0"""A decorator that adds the required ca= pabilities to a test case or test suite.

diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/t= estbed_model/linux_session.py
index 604245d855..1f11c3e740 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -22,7 +22,7 @@
=C2=A0 =C2=A0 =C2=A0InternalError,
=C2=A0 =C2=A0 =C2=A0RemoteCommandExecutionError,
=C2=A0)
-from framework.testbed_model.os_session import PortInfo
+from framework.testbed_model.port import PortInfo
=C2=A0from framework.utils import expand_range

=C2=A0from .cpu import LogicalCore
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/test= bed_model/os_session.py
index b6e03aa83d..c1872880da 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -31,13 +31,9 @@

=C2=A0from framework.config.node import NodeConfiguration
=C2=A0from framework.logger import DTSLogger
-from framework.remote_session import (
-=C2=A0 =C2=A0 InteractiveRemoteSession,
-=C2=A0 =C2=A0 RemoteSession,
-=C2=A0 =C2=A0 create_interactive_session,
-=C2=A0 =C2=A0 create_remote_session,
-)
-from framework.remote_session.remote_session import CommandResult
+from framework.remote_session.interactive_remote_session import Interactiv= eRemoteSession
+from framework.remote_session.remote_session import CommandResult, RemoteS= ession
+from framework.remote_session.ssh_session import SSHSession
=C2=A0from framework.settings import SETTINGS
=C2=A0from framework.utils import MesonArgs, TarCompressionFormat

@@ -129,8 +125,8 @@ def __init__(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self._config =3D node_config
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self.name =3D name
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self._logger =3D logger
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.remote_session =3D create_remote_session(= node_config, name, logger)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.interactive_session =3D create_interactiv= e_session(node_config, logger)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.remote_session =3D SSHSession(node_config= , name, logger)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.interactive_session =3D InteractiveRemote= Session(node_config, logger)

=C2=A0 =C2=A0 =C2=A0def is_alive(self) -> bool:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""Check whether the under= lying remote session is still responding."""
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbe= d_model/topology.py
index 899ea0ad3a..09303a1f08 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -11,33 +11,17 @@
=C2=A0from collections import defaultdict
=C2=A0from collections.abc import Iterator
=C2=A0from dataclasses import dataclass
-from enum import Enum
=C2=A0from typing import Literal, NamedTuple

=C2=A0from typing_extensions import Self

+from api.capabilities import LinkTopology
=C2=A0from framework.exception import ConfigurationError, InternalError
=C2=A0from framework.testbed_model.node import Node

=C2=A0from .port import DriverKind, Port, PortConfig


-class TopologyType(int, Enum):
-=C2=A0 =C2=A0 """Supported topology types."""= ;
-
-=C2=A0 =C2=A0 #: A topology with no Traffic Generator.
-=C2=A0 =C2=A0 no_link =3D 0
-=C2=A0 =C2=A0 #: A topology with one physical link between the SUT node an= d the TG node.
-=C2=A0 =C2=A0 one_link =3D 1
-=C2=A0 =C2=A0 #: A topology with two physical links between the Sut node a= nd the TG node.
-=C2=A0 =C2=A0 two_links =3D 2
-
-=C2=A0 =C2=A0 @classmethod
-=C2=A0 =C2=A0 def default(cls) -> "TopologyType":
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """The default topology require= d by test cases if not specified otherwise."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return cls.two_links
-
-
=C2=A0class PortLink(NamedTuple):
=C2=A0 =C2=A0 =C2=A0"""The physical, cabled connection betwe= en the ports."""

@@ -71,7 +55,7 @@ class Topology:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0tg_ports: The TG ports.
=C2=A0 =C2=A0 =C2=A0"""

-=C2=A0 =C2=A0 type: TopologyType
+=C2=A0 =C2=A0 type: LinkTopology
=C2=A0 =C2=A0 =C2=A0sut_ports: list[Port]
=C2=A0 =C2=A0 =C2=A0tg_ports: list[Port]
=C2=A0 =C2=A0 =C2=A0pf_ports: list[Port]
@@ -87,15 +71,15 @@ def from_port_links(cls, port_links: Iterator[PortLink]= ) -> Self:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Raises:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ConfigurationError: If an u= nsupported link topology is supplied.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 type =3D TopologyType.no_link
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 type =3D LinkTopology.NO_LINK

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if port_link :=3D next(port_links, None):=
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 type =3D TopologyType.one_link +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 type =3D LinkTopology.ONE_LINK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sut_ports =3D [port_link.su= t_port]
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0tg_ports =3D [port_link.tg_= port]

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if port_link :=3D next(port= _links, None):
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 type =3D TopologyT= ype.two_links
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 type =3D LinkTopol= ogy.TWO_LINKS
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sut_ports.app= end(port_link.sut_port)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0tg_ports.appe= nd(port_link.tg_port)

@@ -268,9 +252,9 @@ def sut_port_ingress(self) -> Port:
=C2=A0 =C2=A0 =C2=A0@property
=C2=A0 =C2=A0 =C2=A0def sut_port_egress(self) -> Port:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""The egress port of the = SUT node."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self.sut_ports[1 if self.type is Topolo= gyType.two_links else 0]
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self.sut_ports[1 if self.type is LinkTo= pology.TWO_LINKS else 0]

=C2=A0 =C2=A0 =C2=A0@property
=C2=A0 =C2=A0 =C2=A0def tg_port_ingress(self) -> Port:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""The ingress port of the= TG node."""
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self.tg_ports[1 if self.type is Topolog= yType.two_links else 0]
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return self.tg_ports[1 if self.type is LinkTop= ology.TWO_LINKS else 0]
--
2.39.5

--000000000000d23d9c063f2ccb36--