[v5,1/1] dts: add text parser for testpmd verbose output
Checks
Commit Message
From: Jeremy Spewock <jspewock@iol.unh.edu>
Multiple test suites from the old DTS framework rely on being able to
consume and interpret the verbose output of testpmd. The new framework
doesn't have an elegant way for handling the verbose output, but test
suites are starting to be written that rely on it. This patch creates a
TextParser class that can be used to extract the verbose information
from any testpmd output and also adjusts the `stop` method of the shell
to return all output that it collected.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 525 +++++++++++++++++-
dts/framework/utils.py | 6 +
2 files changed, 529 insertions(+), 2 deletions(-)
Comments
> diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
> @@ -577,6 +577,497 @@ class TestPmdPortStats(TextParser):
> tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
>
>
> +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.
> + """
> + #: No information about the RX IP checksum.
> + RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = 1 << 23
Good idea with the UKNOWN flag values.
> + #: The IP checksum in the packet is wrong.
> + RTE_MBUF_F_RX_IP_CKSUM_BAD = 1 << 4
> + #: The IP checksum in the packet is valid.
> + RTE_MBUF_F_RX_IP_CKSUM_GOOD = 1 << 7
I see you kept the order and just used the corresponding flag values.
Makes sense.
> + #:
> + RTE_MBUF_F_TX_TUNNEL_VXLAN = 1 << 45
> + #:
> + RTE_MBUF_F_TX_TUNNEL_GRE = 2 << 45
> + #:
> + RTE_MBUF_F_TX_TUNNEL_IPIP = 3 << 45
> + #:
> + RTE_MBUF_F_TX_TUNNEL_GENEVE = 4 << 45
> + """ TX packet with MPLS-in-UDP RFC 7510 header. """
This should be one line below after :#
> + #:
> + RTE_MBUF_F_TX_TUNNEL_MPLSINUDP = 5 << 45
> + #:
> + RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE = 6 << 45
> + #:
> + RTE_MBUF_F_TX_TUNNEL_GTP = 7 << 45
> + #:
> + RTE_MBUF_F_TX_TUNNEL_ESP = 8 << 45
So the DPDK code mixes values withing flags? Would this work? We have to
be careful with how we use this:
PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_VXLAN |
PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_GRE ==
PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_IPIP
True
PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_VXLAN |
PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_GRE is
PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_IPIP
True
PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_VXLAN in
PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_IPIP
True
The combination of 1 | 2 == 3, even identity returns True and one flag
is part of another. If we're looking at verbose_output.ol_flags and
checking the RTE_MBUF_F_TX_TUNNEL_VXLAN flag, True would be returned for
all flag that have the first bit set:
RTE_MBUF_F_TX_TUNNEL_VXLAN
RTE_MBUF_F_TX_TUNNEL_IPIP
RTE_MBUF_F_TX_TUNNEL_MPLSINUDP
RTE_MBUF_F_TX_TUNNEL_GTP
Do you know how this is handled in DPDK? Or how testpmd processes this
to return the proper flag?
This mixing seems pretty wild to me (I guess this is to not waste space,
since ULL is max 64 bits). We need to think this through thoroughly.
> + #: TCP cksum of TX pkt. Computed by NIC.
> + RTE_MBUF_F_TX_TCP_CKSUM = 1 << 52
> + #: SCTP cksum of TX pkt. Computed by NIC.
> + RTE_MBUF_F_TX_SCTP_CKSUM = 2 << 52
> + #: UDP cksum of TX pkt. Computed by NIC.
> + RTE_MBUF_F_TX_UDP_CKSUM = 3 << 52
This is the same thing as above.
> + @classmethod
> + def from_str_list(cls, arr: list[str]) -> Self:
> + """Makes an instance from a list containing the flag members.
> +
> + Args:
> + arr: A list of strings containing ol_flag values.
> +
> + Returns:
> + A new instance of the flag.
> + """
> + flag = cls(0)
> + for name in arr:
> + if hasattr(cls, name):
So you used hasattr instead of cls[name] in cls. Is this to avoid the
exception? I now realize that if we could ignore the exception then we
won't need the condition.
The question is when the exception would be raised, or, in other words,
what should we do when hasattr(cls, name) is False. If I understand this
correctly, is it's False, then name is not among the flags and that
means testpmd returned an unsupported flag, which shouldn't happen, but
if it does in the future, we would be better off throwing an exception,
or at very least, log a warning, so that we have an indication that we
need to add support for a new flag.
> + flag |= 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.wrap(TextParser.find(r"ol_flags: ([^\n]+)"), str.split),
> + cls.from_str_list,
> + )
The RSSOffloadTypesFlag does the split in its from_list_string method.
Do we want to do the same here?
Maybe could create a ParsableFlag (or Creatable? Or something else)
superclass that would implement these from_* methods (from_list_string,
from_str) and subclass it. Flags should be subclassable if they don't
contain members.
The superclass would be useful so that we don't redefine the same method
over and over and so that it's clear what's already available.
> @@ -656,6 +1147,9 @@ def stop(self, verify: bool = True) -> None:
> Raises:
> InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
> forwarding results in an error.
> +
> + Returns:
> + Output gathered from sending the stop command.
This not just from sending the stop command, but everything else that
preceded (when collecting the verbose output), right?
> diff --git a/dts/framework/utils.py b/dts/framework/utils.py
> @@ -27,6 +27,12 @@
> from .exception import ConfigurationError
>
> REGEX_FOR_PCI_ADDRESS: str = "/[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}/"
> +_REGEX_FOR_COLON_SEP_MAC: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
> +_REGEX_FOR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}-){5,7}[\da-fA-F]{2}"
{5,7} should be just 5 repetitions. When could it be more?
> diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
> @@ -577,6 +577,497 @@ class TestPmdPortStats(TextParser):
> tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
>
>
> +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
Another minor improvement: we should put the file path into double
backticks. Looking at DPDK docs, that's what they use for file paths, so
``lib/mbuf/rte_mbuf_core.h``.
There's also a References section that supported by google docstrings. I
don't know exactly what to put in it, but I'll try to use it in my
patch. I imagine a list of files (or functions) would be useful - we
could put the DPDK sources and testpmd functions that are relevant there.
On Thu, Sep 19, 2024 at 5:02 AM Juraj Linkeš <juraj.linkes@pantheon.tech> wrote:
>
> > diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
>
> > @@ -577,6 +577,497 @@ class TestPmdPortStats(TextParser):
> > tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
> >
> >
> > +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.
> > + """
>
> > + #: No information about the RX IP checksum.
> > + RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = 1 << 23
>
> Good idea with the UKNOWN flag values.
>
> > + #: The IP checksum in the packet is wrong.
> > + RTE_MBUF_F_RX_IP_CKSUM_BAD = 1 << 4
> > + #: The IP checksum in the packet is valid.
> > + RTE_MBUF_F_RX_IP_CKSUM_GOOD = 1 << 7
>
> I see you kept the order and just used the corresponding flag values.
> Makes sense.
>
>
> > + #:
> > + RTE_MBUF_F_TX_TUNNEL_VXLAN = 1 << 45
> > + #:
> > + RTE_MBUF_F_TX_TUNNEL_GRE = 2 << 45
> > + #:
> > + RTE_MBUF_F_TX_TUNNEL_IPIP = 3 << 45
> > + #:
> > + RTE_MBUF_F_TX_TUNNEL_GENEVE = 4 << 45
> > + """ TX packet with MPLS-in-UDP RFC 7510 header. """
>
> This should be one line below after :#
Ack
>
>
> > + #:
> > + RTE_MBUF_F_TX_TUNNEL_MPLSINUDP = 5 << 45
> > + #:
> > + RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE = 6 << 45
> > + #:
> > + RTE_MBUF_F_TX_TUNNEL_GTP = 7 << 45
> > + #:
> > + RTE_MBUF_F_TX_TUNNEL_ESP = 8 << 45
>
> So the DPDK code mixes values withing flags? Would this work? We have to
> be careful with how we use this:
> PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_VXLAN |
> PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_GRE ==
> PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_IPIP
> True
>
> PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_VXLAN |
> PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_GRE is
> PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_IPIP
> True
>
> PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_VXLAN in
> PacketOffloadFlag.RTE_MBUF_F_TX_TUNNEL_IPIP
> True
>
> The combination of 1 | 2 == 3, even identity returns True and one flag
> is part of another. If we're looking at verbose_output.ol_flags and
> checking the RTE_MBUF_F_TX_TUNNEL_VXLAN flag, True would be returned for
> all flag that have the first bit set:
Right, I recognized this was also weird and I questioned it when I was
writing the values, but I guess my assumption was that if DPDK was
using mixtures of these values which they are getting just through bit
masks, it would only make sense if they were mutually exclusive (or at
least that certain combinations aren't possible that would cause these
problems). I can do some more digging on this though to try and see
where they are used and if that is the case. They don't seem like
mutually exclusive values to me, so there must be some way they are
being very careful about it.
> RTE_MBUF_F_TX_TUNNEL_VXLAN
> RTE_MBUF_F_TX_TUNNEL_IPIP
> RTE_MBUF_F_TX_TUNNEL_MPLSINUDP
> RTE_MBUF_F_TX_TUNNEL_GTP
>
> Do you know how this is handled in DPDK? Or how testpmd processes this
> to return the proper flag?
>
> This mixing seems pretty wild to me (I guess this is to not waste space,
> since ULL is max 64 bits). We need to think this through thoroughly.
I agree that it is pretty outlandish to combine these values like
this, but they must have a clever way of handling it.
>
>
> > + #: TCP cksum of TX pkt. Computed by NIC.
> > + RTE_MBUF_F_TX_TCP_CKSUM = 1 << 52
> > + #: SCTP cksum of TX pkt. Computed by NIC.
> > + RTE_MBUF_F_TX_SCTP_CKSUM = 2 << 52
> > + #: UDP cksum of TX pkt. Computed by NIC.
> > + RTE_MBUF_F_TX_UDP_CKSUM = 3 << 52
>
> This is the same thing as above.
>
>
> > + @classmethod
> > + def from_str_list(cls, arr: list[str]) -> Self:
> > + """Makes an instance from a list containing the flag members.
> > +
> > + Args:
> > + arr: A list of strings containing ol_flag values.
> > +
> > + Returns:
> > + A new instance of the flag.
> > + """
> > + flag = cls(0)
> > + for name in arr:
> > + if hasattr(cls, name):
>
> So you used hasattr instead of cls[name] in cls. Is this to avoid the
> exception? I now realize that if we could ignore the exception then we
> won't need the condition.
Yes, I was trying to just avoid the exception being thrown.
>
> The question is when the exception would be raised, or, in other words,
> what should we do when hasattr(cls, name) is False. If I understand this
> correctly, is it's False, then name is not among the flags and that
> means testpmd returned an unsupported flag, which shouldn't happen, but
> if it does in the future, we would be better off throwing an exception,
> or at very least, log a warning, so that we have an indication that we
> need to add support for a new flag.
This is a good point. Realistically if it is ever false that would
mean we have a gap in implementation. I like the idea of flagging a
loud warning over throwing an exception in this case though since if
we threw an exception that would stop all test cases that use OL flags
to stop working even if they don't require the new flag. That would
definitely get the problem fixed sooner, but would also shutdown
automated testing until it then.
>
> > + flag |= 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.wrap(TextParser.find(r"ol_flags: ([^\n]+)"), str.split),
> > + cls.from_str_list,
> > + )
>
> The RSSOffloadTypesFlag does the split in its from_list_string method.
> Do we want to do the same here?
>
> Maybe could create a ParsableFlag (or Creatable? Or something else)
> superclass that would implement these from_* methods (from_list_string,
> from_str) and subclass it. Flags should be subclassable if they don't
> contain members.
>
> The superclass would be useful so that we don't redefine the same method
> over and over and so that it's clear what's already available.
I like this idea a lot. Basically all of these flags that are used in
parsers are going to need something like that which is going to be
basically the same so just implementing it one time would be great.
I'm not sure if it fits the scope of this series though, do you think
I should write it and add it here or in a separate patch?
>
>
> > @@ -656,6 +1147,9 @@ def stop(self, verify: bool = True) -> None:
> > Raises:
> > InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
> > forwarding results in an error.
> > +
> > + Returns:
> > + Output gathered from sending the stop command.
>
> This not just from sending the stop command, but everything else that
> preceded (when collecting the verbose output), right?
Technically yes, but that's just due to the nature of how interactive
shells aren't perfect when it comes to asynchronous output. That's why
I tried to be sneaky and say that it is the "output gathered from
sending the stop command" trying to imply that it is not just the
output of the `stop` command, but all the output that is gathered from
sending it. I can update this though.
>
>
> > diff --git a/dts/framework/utils.py b/dts/framework/utils.py
>
> > @@ -27,6 +27,12 @@
> > from .exception import ConfigurationError
> >
> > REGEX_FOR_PCI_ADDRESS: str = "/[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}/"
> > +_REGEX_FOR_COLON_SEP_MAC: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
> > +_REGEX_FOR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}-){5,7}[\da-fA-F]{2}"
>
> {5,7} should be just 5 repetitions. When could it be more?
I added it for EUI-64 addresses, but maybe this isn't very relevant
here since I just read that they are encouraged on non-ethernet
devices. I can remove it if it doesn't seem worth it to capture.
>
On Thu, Sep 19, 2024 at 8:35 AM Juraj Linkeš <juraj.linkes@pantheon.tech> wrote:
>
> > diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
>
> > @@ -577,6 +577,497 @@ class TestPmdPortStats(TextParser):
> > tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
> >
> >
> > +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
>
> Another minor improvement: we should put the file path into double
> backticks. Looking at DPDK docs, that's what they use for file paths, so
> ``lib/mbuf/rte_mbuf_core.h``.
good idea, I didn't know there was already a format for doing this.
>
> There's also a References section that supported by google docstrings. I
> don't know exactly what to put in it, but I'll try to use it in my
> patch. I imagine a list of files (or functions) would be useful - we
> could put the DPDK sources and testpmd functions that are relevant there.
I'll have to read about this as well, I didn't know that was a thing either :).
>
>>
>> The question is when the exception would be raised, or, in other words,
>> what should we do when hasattr(cls, name) is False. If I understand this
>> correctly, is it's False, then name is not among the flags and that
>> means testpmd returned an unsupported flag, which shouldn't happen, but
>> if it does in the future, we would be better off throwing an exception,
>> or at very least, log a warning, so that we have an indication that we
>> need to add support for a new flag.
>
> This is a good point. Realistically if it is ever false that would
> mean we have a gap in implementation. I like the idea of flagging a
> loud warning over throwing an exception in this case though since if
> we threw an exception that would stop all test cases that use OL flags
> to stop working even if they don't require the new flag. That would
> definitely get the problem fixed sooner, but would also shutdown
> automated testing until it then.
>
It's a tradeoff between risking CI being affected (if a new flag is
added without also adding it to DTS) and how noticable the warning is. I
guess we can implement something in CI that will look for warnings like
these?
>>
>>> + flag |= 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.wrap(TextParser.find(r"ol_flags: ([^\n]+)"), str.split),
>>> + cls.from_str_list,
>>> + )
>>
>> The RSSOffloadTypesFlag does the split in its from_list_string method.
>> Do we want to do the same here?
>>
>> Maybe could create a ParsableFlag (or Creatable? Or something else)
>> superclass that would implement these from_* methods (from_list_string,
>> from_str) and subclass it. Flags should be subclassable if they don't
>> contain members.
>>
>> The superclass would be useful so that we don't redefine the same method
>> over and over and so that it's clear what's already available.
>
> I like this idea a lot. Basically all of these flags that are used in
> parsers are going to need something like that which is going to be
> basically the same so just implementing it one time would be great.
> I'm not sure if it fits the scope of this series though, do you think
> I should write it and add it here or in a separate patch?
>
A separate patch seems better, as it touches different parts of the
code. We should probably implement the same logic in this patch (without
the exception or warning and with the same if condition) and then make
changes in the other patch.
>>
>>
>>> @@ -656,6 +1147,9 @@ def stop(self, verify: bool = True) -> None:
>>> Raises:
>>> InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
>>> forwarding results in an error.
>>> +
>>> + Returns:
>>> + Output gathered from sending the stop command.
>>
>> This not just from sending the stop command, but everything else that
>> preceded (when collecting the verbose output), right?
>
> Technically yes, but that's just due to the nature of how interactive
> shells aren't perfect when it comes to asynchronous output. That's why
> I tried to be sneaky and say that it is the "output gathered from
> sending the stop command" trying to imply that it is not just the
> output of the `stop` command, but all the output that is gathered from
> sending it. I can update this though.
>
>>
>>
>>> diff --git a/dts/framework/utils.py b/dts/framework/utils.py
>>
>>> @@ -27,6 +27,12 @@
>>> from .exception import ConfigurationError
>>>
>>> REGEX_FOR_PCI_ADDRESS: str = "/[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}/"
>>> +_REGEX_FOR_COLON_SEP_MAC: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
>>> +_REGEX_FOR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}-){5,7}[\da-fA-F]{2}"
>>
>> {5,7} should be just 5 repetitions. When could it be more?
>
> I added it for EUI-64 addresses, but maybe this isn't very relevant
> here since I just read that they are encouraged on non-ethernet
> devices. I can remove it if it doesn't seem worth it to capture.
>
From what I gather the EUI-64 address is composed from MAC addresses,
but it's a different identifier. I'd say if we ever need it we can add
it as a separate regex (and look for both if we need to).
@@ -31,7 +31,7 @@
from framework.settings import SETTINGS
from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
from framework.testbed_model.sut_node import SutNode
-from framework.utils import StrEnum
+from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
class TestPmdDevice:
@@ -577,6 +577,497 @@ class TestPmdPortStats(TextParser):
tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
+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.
+ """
+
+ # 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 = auto()
+
+ #: RX packet with RSS hash result.
+ RTE_MBUF_F_RX_RSS_HASH = auto()
+
+ #: RX packet with FDIR match indicate.
+ RTE_MBUF_F_RX_FDIR = auto()
+
+ #: This flag is set when the outermost IP header checksum is detected as wrong by the hardware.
+ RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD = 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 = auto()
+
+ #: No information about the RX IP checksum.
+ RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = 1 << 23
+ #: The IP checksum in the packet is wrong.
+ RTE_MBUF_F_RX_IP_CKSUM_BAD = 1 << 4
+ #: The IP checksum in the packet is valid.
+ RTE_MBUF_F_RX_IP_CKSUM_GOOD = 1 << 7
+ #: The IP checksum is not correct in the packet data, but the integrity of the IP header is
+ #: verified.
+ RTE_MBUF_F_RX_IP_CKSUM_NONE = RTE_MBUF_F_RX_IP_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_GOOD
+
+ #: No information about the RX L4 checksum.
+ RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = 1 << 24
+ #: The L4 checksum in the packet is wrong.
+ RTE_MBUF_F_RX_L4_CKSUM_BAD = 1 << 3
+ #: The L4 checksum in the packet is valid.
+ RTE_MBUF_F_RX_L4_CKSUM_GOOD = 1 << 8
+ #: The L4 checksum is not correct in the packet data, but the integrity of the L4 data is
+ #: verified.
+ RTE_MBUF_F_RX_L4_CKSUM_NONE = RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD
+
+ #: RX IEEE1588 L2 Ethernet PT Packet.
+ RTE_MBUF_F_RX_IEEE1588_PTP = 1 << 9
+ #: RX IEEE1588 L2/L4 timestamped packet.
+ RTE_MBUF_F_RX_IEEE1588_TMST = 1 << 10
+
+ #: FD id reported if FDIR match.
+ RTE_MBUF_F_RX_FDIR_ID = 1 << 13
+ #: Flexible bytes reported if FDIR match.
+ RTE_MBUF_F_RX_FDIR_FLX = 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_STRIPPED 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 = 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 = auto()
+
+ #: Indicate that security offload processing was applied on the RX packet.
+ RTE_MBUF_F_RX_SEC_OFFLOAD = 1 << 18
+ #: Indicate that security offload processing failed on the RX packet.
+ RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED = 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 = auto()
+
+ #: No info about the outer RX L4 checksum
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = 1 << 25
+ #: The outer L4 checksum in the packet is wrong
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = 1 << 21
+ #: The outer L4 checksum in the packet is valid
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = 1 << 22
+ #: Invalid outer L4 checksum state.
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_INVALID = (
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD | RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD
+ )
+
+ # 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 = 1 << 41
+
+ #: UDP Fragmentation Offload flag. This flag is used for enabling UDP fragmentation in SW or in
+ #: HW.
+ RTE_MBUF_F_TX_UDP_SEG = 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 = 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 = auto()
+
+ """
+ Bits 45:48 used for the tunnel type. 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 = 1 << 45
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GRE = 2 << 45
+ #:
+ RTE_MBUF_F_TX_TUNNEL_IPIP = 3 << 45
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GENEVE = 4 << 45
+ """ TX packet with MPLS-in-UDP RFC 7510 header. """
+ #:
+ RTE_MBUF_F_TX_TUNNEL_MPLSINUDP = 5 << 45
+ #:
+ RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE = 6 << 45
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GTP = 7 << 45
+ #:
+ RTE_MBUF_F_TX_TUNNEL_ESP = 8 << 45
+ #: 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.
+ RTE_MBUF_F_TX_TUNNEL_IP = 0xD << 45
+ #: 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.
+ RTE_MBUF_F_TX_TUNNEL_UDP = auto()
+
+ #: Double VLAN insertion (QinQ) request to driver, driver may offload 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 = 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 flag 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 = auto()
+ #: TX IEEE1588 packet to timestamp.
+ RTE_MBUF_F_TX_IEEE1588_TMST = auto()
+
+ """
+ Bits 52+53 used for L4 packet type with checksum enabled: 00: Reserved,
+ 01: TCP checksum, 10: SCTP checksum, 11: UDP checksum. 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. Originally 0 in rte_mbuf_core.h but changed for uniqueness.
+ RTE_MBUF_F_TX_L4_NO_CKSUM = 1 << 61
+ #: TCP cksum of TX pkt. Computed by NIC.
+ RTE_MBUF_F_TX_TCP_CKSUM = 1 << 52
+ #: SCTP cksum of TX pkt. Computed by NIC.
+ RTE_MBUF_F_TX_SCTP_CKSUM = 2 << 52
+ #: UDP cksum of TX pkt. Computed by NIC.
+ RTE_MBUF_F_TX_UDP_CKSUM = 3 << 52
+
+ #: 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 = 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 = 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 = 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 = auto()
+
+ #: Offload the IP checksum of an external header in the hardware. The 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 = 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 = 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 = auto()
+
+ @classmethod
+ def from_str_list(cls, arr: list[str]) -> Self:
+ """Makes an instance from a list containing the flag members.
+
+ Args:
+ arr: A list of strings containing ol_flag values.
+
+ Returns:
+ A new instance of the flag.
+ """
+ flag = cls(0)
+ for name in arr:
+ if hasattr(cls, name):
+ flag |= 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.wrap(TextParser.find(r"ol_flags: ([^\n]+)"), str.split),
+ cls.from_str_list,
+ )
+
+
+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 methods rte_get_ptype_*_name in rte_mbuf_ptype.c.
+ """
+
+ # L2
+ #: Ethernet packet type. This is used for outer packet for tunneling cases.
+ L2_ETHER = auto()
+ #: Ethernet packet type for time sync.
+ L2_ETHER_TIMESYNC = auto()
+ #: ARP (Address Resolution Protocol) packet type.
+ L2_ETHER_ARP = auto()
+ #: LLDP (Link Layer Discovery Protocol) packet type.
+ L2_ETHER_LLDP = auto()
+ #: NSH (Network Service Header) packet type.
+ L2_ETHER_NSH = auto()
+ #: VLAN packet type.
+ L2_ETHER_VLAN = auto()
+ #: QinQ packet type.
+ L2_ETHER_QINQ = auto()
+ #: PPPOE packet type.
+ L2_ETHER_PPPOE = auto()
+ #: FCoE packet type..
+ L2_ETHER_FCOE = auto()
+ #: MPLS packet type.
+ L2_ETHER_MPLS = auto()
+ #: No L2 packet information.
+ L2_UNKNOWN = 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 = auto()
+ #: IP (Internet Protocol) version 4 packet type. This is used for outer packet for tunneling
+ #: cases, and contains header options.
+ L3_IPV4_EXT = 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 = 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 = auto()
+ #: IP (Internet Protocol) version 6 packet type. This is used for outer packet for tunneling
+ #: cases, and contains extension headers.
+ L3_IPV6_EXT = 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 = auto()
+ #: No L3 packet information.
+ L3_UNKNOWN = auto()
+
+ # L4
+ #: TCP (Transmission Control Protocol) packet type. This is used for outer packet for tunneling
+ #: cases.
+ L4_TCP = auto()
+ #: UDP (User Datagram Protocol) packet type. This is used for outer packet for tunneling cases.
+ L4_UDP = 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 = auto()
+ #: SCTP (Stream Control Transmission Protocol) packet type. This is used for outer packet for
+ #: tunneling cases.
+ L4_SCTP = auto()
+ #: ICMP (Internet Control Message Protocol) packet type. This is used for outer packet for
+ #: tunneling cases.
+ L4_ICMP = 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 = auto()
+ #: IGMP (Internet Group Management Protocol) packet type.
+ L4_IGMP = auto()
+ #: No L4 packet information.
+ L4_UNKNOWN = auto()
+
+ # Tunnel
+ #: IP (Internet Protocol) in IP (Internet Protocol) tunneling packet type.
+ TUNNEL_IP = auto()
+ #: GRE (Generic Routing Encapsulation) tunneling packet type.
+ TUNNEL_GRE = auto()
+ #: VXLAN (Virtual eXtensible Local Area Network) tunneling packet type.
+ TUNNEL_VXLAN = auto()
+ #: NVGRE (Network Virtualization using Generic Routing Encapsulation) tunneling packet type.
+ TUNNEL_NVGRE = auto()
+ #: GENEVE (Generic Network Virtualization Encapsulation) tunneling packet type.
+ TUNNEL_GENEVE = auto()
+ #: Tunneling packet type of Teredo, VXLAN (Virtual eXtensible Local Area Network) or GRE
+ #: (Generic Routing Encapsulation) could be recognized as this packet type, if they can not be
+ #: recognized independently as of hardware capability.
+ TUNNEL_GRENAT = auto()
+ #: GTP-C (GPRS Tunnelling Protocol) control tunneling packet type.
+ TUNNEL_GTPC = auto()
+ #: GTP-U (GPRS Tunnelling Protocol) user data tunneling packet type.
+ TUNNEL_GTPU = auto()
+ #: ESP (IP Encapsulating Security Payload) tunneling packet type.
+ TUNNEL_ESP = auto()
+ #: L2TP (Layer 2 Tunneling Protocol) tunneling packet type.
+ TUNNEL_L2TP = auto()
+ #: VXLAN-GPE (VXLAN Generic Protocol Extension) tunneling packet type.
+ TUNNEL_VXLAN_GPE = auto()
+ #: MPLS-in-UDP tunneling packet type (RFC 7510).
+ TUNNEL_MPLS_IN_UDP = auto()
+ #: MPLS-in-GRE tunneling packet type (RFC 4023).
+ TUNNEL_MPLS_IN_GRE = auto()
+ #: No tunnel information found on the packet.
+ TUNNEL_UNKNOWN = auto()
+
+ # Inner L2
+ #: Ethernet packet type. This is used for inner packet type only.
+ INNER_L2_ETHER = auto()
+ #: Ethernet packet type with VLAN (Virtual Local Area Network) tag.
+ INNER_L2_ETHER_VLAN = auto()
+ #: QinQ packet type.
+ INNER_L2_ETHER_QINQ = auto()
+ #: No inner L2 information found on the packet.
+ INNER_L2_UNKNOWN = 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 = auto()
+ #: IP (Internet Protocol) version 4 packet type. This is used for inner packet only, and
+ #: contains header options.
+ INNER_L3_IPV4_EXT = 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 = 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 = auto()
+ #: IP (Internet Protocol) version 6 packet type. This is used for inner packet only, and
+ #: contains extension headers.
+ INNER_L3_IPV6_EXT = 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 = auto()
+ #: No inner L3 information found on the packet.
+ INNER_L3_UNKNOWN = auto()
+
+ # Inner L4
+ #: TCP (Transmission Control Protocol) packet type. This is used for inner packet only.
+ INNER_L4_TCP = auto()
+ #: UDP (User Datagram Protocol) packet type. This is used for inner packet only.
+ INNER_L4_UDP = 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 = auto()
+ #: SCTP (Stream Control Transmission Protocol) packet type. This is used for inner packet only.
+ INNER_L4_SCTP = auto()
+ #: ICMP (Internet Control Message Protocol) packet type. This is used for inner packet only.
+ INNER_L4_ICMP = 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 = auto()
+ #: No inner L4 information found on the packet.
+ INNER_L4_UNKNOWN = auto()
+
+ @classmethod
+ def from_str_list(cls, arr: list[str]) -> Self:
+ """Makes an instance from a list containing the flag members.
+
+ Args:
+ arr: A list of strings containing packet types.
+
+ Returns:
+ A new instance of the flag.
+ """
+ flag = cls(0)
+ for name in arr:
+ if hasattr(cls, name):
+ flag |= cls[name]
+ return flag
+
+ @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.wrap(TextParser.find(f"{'hw' if hw else 'sw'} ptype: ([^-]+)"), str.split),
+ cls.from_str_list,
+ )
+
+
+@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 = field(metadata=TextParser.find_int(r"port (\d+)/queue \d+"))
+ #: ID of the queue that handled the packet.
+ queue_id: int = field(metadata=TextParser.find_int(r"port \d+/queue (\d+)"))
+ #: Whether the packet was received or sent by the queue/port.
+ was_received: bool = field(metadata=TextParser.find(r"received \d+ packets"))
+ #:
+ src_mac: str = field(metadata=TextParser.find(f"src=({REGEX_FOR_MAC_ADDRESS})"))
+ #:
+ dst_mac: str = field(metadata=TextParser.find(f"dst=({REGEX_FOR_MAC_ADDRESS})"))
+ #: Memory pool the packet was handled on.
+ pool: str = field(metadata=TextParser.find(r"pool=(\S+)"))
+ #: Packet type in hex.
+ p_type: int = field(metadata=TextParser.find_int(r"type=(0x[a-fA-F\d]+)"))
+ #:
+ length: int = field(metadata=TextParser.find_int(r"length=(\d+)"))
+ #: Number of segments in the packet.
+ nb_segs: int = field(metadata=TextParser.find_int(r"nb_segs=(\d+)"))
+ #: Hardware packet type.
+ hw_ptype: RtePTypes = field(metadata=RtePTypes.make_parser(hw=True))
+ #: Software packet type.
+ sw_ptype: RtePTypes = field(metadata=RtePTypes.make_parser(hw=False))
+ #:
+ l2_len: int = field(metadata=TextParser.find_int(r"l2_len=(\d+)"))
+ #:
+ ol_flags: PacketOffloadFlag = field(metadata=PacketOffloadFlag.make_parser())
+ #: RSS hash of the packet in hex.
+ rss_hash: int | None = field(
+ default=None, metadata=TextParser.find_int(r"RSS hash=(0x[a-fA-F\d]+)")
+ )
+ #: RSS queue that handled the packet in hex.
+ rss_queue: int | None = field(
+ default=None, metadata=TextParser.find_int(r"RSS queue=(0x[a-fA-F\d]+)")
+ )
+ #:
+ l3_len: int | None = field(default=None, metadata=TextParser.find_int(r"l3_len=(\d+)"))
+ #:
+ l4_len: int | None = field(default=None, metadata=TextParser.find_int(r"l4_len=(\d+)"))
+
+
class TestPmdShell(DPDKShell):
"""Testpmd interactive shell.
@@ -645,7 +1136,7 @@ def start(self, verify: bool = True) -> None:
"Not all ports came up after starting packet forwarding in testpmd."
)
- def stop(self, verify: bool = True) -> None:
+ def stop(self, verify: bool = True) -> str:
"""Stop packet forwarding.
Args:
@@ -656,6 +1147,9 @@ def stop(self, verify: bool = True) -> None:
Raises:
InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
forwarding results in an error.
+
+ Returns:
+ Output gathered from sending the stop command.
"""
stop_cmd_output = self.send_command("stop")
if verify:
@@ -665,6 +1159,7 @@ def stop(self, verify: bool = True) -> None:
):
self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}")
raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.")
+ return stop_cmd_output
def get_devices(self) -> list[TestPmdDevice]:
"""Get a list of device names that are known to testpmd.
@@ -806,6 +1301,32 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
return TestPmdPortStats.parse(output)
+ @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] = []
+ prev_header: str = ""
+ iter = re.finditer(
+ r"(?P<HEADER>(?:port \d+/queue \d+: (?:received|sent) \d+ packets)?)\s*"
+ r"(?P<PACKET>src=[\w\s=:-]+?ol_flags: [\w ]+)",
+ output,
+ )
+ for match in iter:
+ if match.group("HEADER"):
+ prev_header = match.group("HEADER")
+ out.append(TestPmdVerbosePacket.parse(f"{prev_header}\n{match.group('PACKET')}"))
+ return out
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
@@ -27,6 +27,12 @@
from .exception import ConfigurationError
REGEX_FOR_PCI_ADDRESS: str = "/[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}/"
+_REGEX_FOR_COLON_SEP_MAC: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
+_REGEX_FOR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}-){5,7}[\da-fA-F]{2}"
+_REGEX_FOR_DOT_SEP_MAC: str = r"(?:[\da-fA-F]{4}.){2}[\da-fA-F]{4}"
+REGEX_FOR_MAC_ADDRESS: str = (
+ rf"{_REGEX_FOR_COLON_SEP_MAC}|{_REGEX_FOR_HYPHEN_SEP_MAC}|{_REGEX_FOR_DOT_SEP_MAC}"
+)
def expand_range(range_str: str) -> list[int]: