* [PATCH v1 0/1] dts: testpmd verbose parser
@ 2024-07-29 20:39 jspewock
2024-07-29 20:39 ` [PATCH v1 1/1] dts: add text parser for testpmd verbose output jspewock
` (6 more replies)
0 siblings, 7 replies; 39+ messages in thread
From: jspewock @ 2024-07-29 20:39 UTC (permalink / raw)
To: Luca.Vizzarro, juraj.linkes, npratte, wathsala.vithanage, thomas,
paul.szczepanek, probb, Honnappa.Nagarahalli, yoan.picchi
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This series adds a new text parser that is able to extract all verbose
messages from testpmd output into a more organized data structure.
Jeremy Spewock (1):
dts: add text parser for testpmd verbose output
dts/framework/parser.py | 30 ++++
dts/framework/remote_session/testpmd_shell.py | 146 +++++++++++++++++-
dts/framework/utils.py | 1 +
3 files changed, 175 insertions(+), 2 deletions(-)
--
2.45.2
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v1 1/1] dts: add text parser for testpmd verbose output
2024-07-29 20:39 [PATCH v1 0/1] dts: testpmd verbose parser jspewock
@ 2024-07-29 20:39 ` jspewock
2024-07-30 13:34 ` [PATCH v2 0/1] dts: testpmd verbose parser jspewock
` (5 subsequent siblings)
6 siblings, 0 replies; 39+ messages in thread
From: jspewock @ 2024-07-29 20:39 UTC (permalink / raw)
To: Luca.Vizzarro, juraj.linkes, npratte, wathsala.vithanage, thomas,
paul.szczepanek, probb, Honnappa.Nagarahalli, yoan.picchi
Cc: dev, Jeremy Spewock
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>
---
One thing to note here is I don't love the regex in
extract_verbose_output(). It works great when there is a bunch of
verbose output in a row, but any chunk that isn't followed by another
piece of verbose output will contain everything that comes after it in
the match group. This could be solved by changing the regex to look
ahead only for the next port X/queue Y line instead of also including
the end of the string, and then having another alternate route which is
solely dedicated to the last block of verbose output which greedily
consumes everything until the end of ol_flags, but I didn't want to over
complicate the regex since the text parser will extract the specific
information it needs anyways. For reference, I was thinking it could be
something like this:
r"(port \d+/queue \d+:.*?(?=port \d+/queue \d+)|port \d+/queue \d+:.*ol_flags: [\w ]+)"
but this has a lot of repition (some of which that could be ripped out
with a simple variable) and it is a little more confusing to read I
think.
dts/framework/parser.py | 30 ++++
dts/framework/remote_session/testpmd_shell.py | 146 +++++++++++++++++-
dts/framework/utils.py | 1 +
3 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 741dfff821..0b39025a48 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -160,6 +160,36 @@ def _find(text: str) -> Any:
return ParserFn(TextParser_fn=_find)
+ @staticmethod
+ def find_all(
+ pattern: str | re.Pattern[str],
+ flags: re.RegexFlag = re.RegexFlag(0),
+ ) -> ParserFn:
+ """Makes a parser function that finds all of the regular expression matches in the text.
+
+ If there are no matches found in the text than None will be returned, otherwise a list
+ containing all matches will be returned. Patterns that contain multiple groups will pack
+ the matches for each group into a tuple.
+
+ Args:
+ pattern: The regular expression pattern.
+ flags: The regular expression flags. Ignored if the given pattern is already compiled.
+
+ Returns:
+ A :class:`ParserFn` that can be used as metadata for a dataclass field.
+ """
+ if isinstance(pattern, str):
+ pattern = re.compile(pattern, flags)
+
+ def _find_all(text: str) -> list[str] | None:
+ m = pattern.findall(text)
+ if len(m) == 0:
+ return None
+
+ return m
+
+ return ParserFn(TextParser_fn=_find_all)
+
@staticmethod
def find_int(
pattern: str | re.Pattern[str],
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 43e9f56517..9f09a98490 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -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,128 @@ class TestPmdPortStats(TextParser):
tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
+class VerboseOLFlag(Flag):
+ """Flag representing the OL flags of a packet from Testpmd verbose output."""
+
+ #:
+ RTE_MBUF_F_RX_RSS_HASH = auto()
+
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = auto()
+
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = auto()
+
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_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 ol_flag values.
+
+ Returns:
+ A new instance of the flag.
+ """
+ flag = cls(0)
+ for name in cls.__members__:
+ if name in arr:
+ 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,
+ )
+
+
+@dataclass
+class TestPmdVerbosePacket(TextParser):
+ """Packet information provided by verbose output in Testpmd.
+
+ The "receive/sent queue" information is not included in this dataclass because this value is
+ captured on the outer layer of input found in :class:`TestPmdVerboseOutput`.
+ """
+
+ #:
+ src_mac: str = field(metadata=TextParser.find(r"src=({})".format(REGEX_FOR_MAC_ADDRESS)))
+ #:
+ dst_mac: str = field(metadata=TextParser.find(r"dst=({})".format(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. Collected as a string delimited by whitespace.
+ hw_ptype: str = field(metadata=TextParser.find(r"hw ptype: ([^-]+)"))
+ #: Software packet type. Collected as a string delimited by whitespace.
+ sw_ptype: str = field(metadata=TextParser.find(r"sw ptype: ([^-]+)"))
+ #:
+ l2_len: int = field(metadata=TextParser.find_int(r"l2_len=(\d+)"))
+ #:
+ ol_flags: VerboseOLFlag = field(metadata=VerboseOLFlag.make_parser())
+ #: RSS has 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+)"))
+
+
+@dataclass
+class TestPmdVerboseOutput(TextParser):
+ """Verbose output generated by Testpmd.
+
+ This class is the top level of the output, containing verbose output delimited by
+ "port X/queue Y: sent/received Z packets".
+ """
+
+ #: ID of the port that handled the packet.
+ port_id: int = field(metadata=TextParser.find(r"port (\d+)/queue \d+"))
+ #: ID of the queue that handled the packet.
+ queue_id: int = field(metadata=TextParser.find(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"))
+ #: List of packets handed by the port/queue in this section.
+ packets: list[TestPmdVerbosePacket] = field(
+ metadata=TextParser.wrap(
+ TextParser.find_all(r"(src=[\w\s=:-]+ol_flags: [\w ]+)"),
+ lambda matches_arr: list(map(TestPmdVerbosePacket.parse, matches_arr)),
+ )
+ )
+
+
class TestPmdShell(DPDKShell):
"""Testpmd interactive shell.
@@ -645,7 +767,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 +778,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 +790,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 +932,22 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
return TestPmdPortStats.parse(output)
+ @staticmethod
+ def extract_verbose_output(output: str) -> list[TestPmdVerboseOutput]:
+ """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`.
+ """
+ iter = re.finditer(r"(port \d+/queue \d+:.*?(?=port \d+/queue \d+|$))", output, re.S)
+ return [TestPmdVerboseOutput.parse(s.group(0)) for s in iter]
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 6b5d5a805f..9c64cf497f 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -27,6 +27,7 @@
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_MAC_ADDRESS: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
def expand_range(range_str: str) -> list[int]:
--
2.45.2
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v2 0/1] dts: testpmd verbose parser
2024-07-29 20:39 [PATCH v1 0/1] dts: testpmd verbose parser jspewock
2024-07-29 20:39 ` [PATCH v1 1/1] dts: add text parser for testpmd verbose output jspewock
@ 2024-07-30 13:34 ` jspewock
2024-07-30 13:34 ` [PATCH v2 1/1] dts: add text parser for testpmd verbose output jspewock
2024-08-08 20:36 ` [PATCH v3 0/1] dts: testpmd verbose parser jspewock
` (4 subsequent siblings)
6 siblings, 1 reply; 39+ messages in thread
From: jspewock @ 2024-07-30 13:34 UTC (permalink / raw)
To: yoan.picchi, probb, paul.szczepanek, npratte, thomas,
Honnappa.Nagarahalli, Luca.Vizzarro, juraj.linkes,
wathsala.vithanage
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v2:
* fix queue and port IDs types in the testpmd verbose output dataclass
using find_int().
Jeremy Spewock (1):
dts: add text parser for testpmd verbose output
dts/framework/parser.py | 30 ++++
dts/framework/remote_session/testpmd_shell.py | 146 +++++++++++++++++-
dts/framework/utils.py | 1 +
3 files changed, 175 insertions(+), 2 deletions(-)
--
2.45.2
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-07-30 13:34 ` [PATCH v2 0/1] dts: testpmd verbose parser jspewock
@ 2024-07-30 13:34 ` jspewock
2024-07-30 15:41 ` Nicholas Pratte
` (2 more replies)
0 siblings, 3 replies; 39+ messages in thread
From: jspewock @ 2024-07-30 13:34 UTC (permalink / raw)
To: yoan.picchi, probb, paul.szczepanek, npratte, thomas,
Honnappa.Nagarahalli, Luca.Vizzarro, juraj.linkes,
wathsala.vithanage
Cc: dev, Jeremy Spewock
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>
---
One thing to note here is I don't love the regex in
extract_verbose_output(). It works great when there is a bunch of
verbose output in a row, but any chunk that isn't followed by another
piece of verbose output will contain everything that comes after it in
the match group. This could be solved by changing the regex to look
ahead only for the next port X/queue Y line instead of also including
the end of the string, and then having another alternate route which is
solely dedicated to the last block of verbose output which greedily
consumes everything until the end of ol_flags, but I didn't want to over
complicate the regex since the text parser will extract the specific
information it needs anyways. For reference, I was thinking it could be
something like this:
r"(port \d+/queue \d+:.*?(?=port \d+/queue \d+)|port \d+/queue \d+:.*ol_flags: [\w ]+)"
but this has a lot of repition (some of which that could be ripped out
with a simple variable) and it is a little more confusing to read I
think.
dts/framework/parser.py | 30 ++++
dts/framework/remote_session/testpmd_shell.py | 146 +++++++++++++++++-
dts/framework/utils.py | 1 +
3 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 741dfff821..0b39025a48 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -160,6 +160,36 @@ def _find(text: str) -> Any:
return ParserFn(TextParser_fn=_find)
+ @staticmethod
+ def find_all(
+ pattern: str | re.Pattern[str],
+ flags: re.RegexFlag = re.RegexFlag(0),
+ ) -> ParserFn:
+ """Makes a parser function that finds all of the regular expression matches in the text.
+
+ If there are no matches found in the text than None will be returned, otherwise a list
+ containing all matches will be returned. Patterns that contain multiple groups will pack
+ the matches for each group into a tuple.
+
+ Args:
+ pattern: The regular expression pattern.
+ flags: The regular expression flags. Ignored if the given pattern is already compiled.
+
+ Returns:
+ A :class:`ParserFn` that can be used as metadata for a dataclass field.
+ """
+ if isinstance(pattern, str):
+ pattern = re.compile(pattern, flags)
+
+ def _find_all(text: str) -> list[str] | None:
+ m = pattern.findall(text)
+ if len(m) == 0:
+ return None
+
+ return m
+
+ return ParserFn(TextParser_fn=_find_all)
+
@staticmethod
def find_int(
pattern: str | re.Pattern[str],
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 43e9f56517..dedf1553cf 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -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,128 @@ class TestPmdPortStats(TextParser):
tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
+class VerboseOLFlag(Flag):
+ """Flag representing the OL flags of a packet from Testpmd verbose output."""
+
+ #:
+ RTE_MBUF_F_RX_RSS_HASH = auto()
+
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = auto()
+
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = auto()
+
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_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 ol_flag values.
+
+ Returns:
+ A new instance of the flag.
+ """
+ flag = cls(0)
+ for name in cls.__members__:
+ if name in arr:
+ 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,
+ )
+
+
+@dataclass
+class TestPmdVerbosePacket(TextParser):
+ """Packet information provided by verbose output in Testpmd.
+
+ The "receive/sent queue" information is not included in this dataclass because this value is
+ captured on the outer layer of input found in :class:`TestPmdVerboseOutput`.
+ """
+
+ #:
+ src_mac: str = field(metadata=TextParser.find(r"src=({})".format(REGEX_FOR_MAC_ADDRESS)))
+ #:
+ dst_mac: str = field(metadata=TextParser.find(r"dst=({})".format(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. Collected as a string delimited by whitespace.
+ hw_ptype: str = field(metadata=TextParser.find(r"hw ptype: ([^-]+)"))
+ #: Software packet type. Collected as a string delimited by whitespace.
+ sw_ptype: str = field(metadata=TextParser.find(r"sw ptype: ([^-]+)"))
+ #:
+ l2_len: int = field(metadata=TextParser.find_int(r"l2_len=(\d+)"))
+ #:
+ ol_flags: VerboseOLFlag = field(metadata=VerboseOLFlag.make_parser())
+ #: RSS has 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+)"))
+
+
+@dataclass
+class TestPmdVerboseOutput(TextParser):
+ """Verbose output generated by Testpmd.
+
+ This class is the top level of the output, containing verbose output delimited by
+ "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"))
+ #: List of packets handed by the port/queue in this section.
+ packets: list[TestPmdVerbosePacket] = field(
+ metadata=TextParser.wrap(
+ TextParser.find_all(r"(src=[\w\s=:-]+ol_flags: [\w ]+)"),
+ lambda matches_arr: list(map(TestPmdVerbosePacket.parse, matches_arr)),
+ )
+ )
+
+
class TestPmdShell(DPDKShell):
"""Testpmd interactive shell.
@@ -645,7 +767,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 +778,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 +790,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 +932,22 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
return TestPmdPortStats.parse(output)
+ @staticmethod
+ def extract_verbose_output(output: str) -> list[TestPmdVerboseOutput]:
+ """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`.
+ """
+ iter = re.finditer(r"(port \d+/queue \d+:.*?(?=port \d+/queue \d+|$))", output, re.S)
+ return [TestPmdVerboseOutput.parse(s.group(0)) for s in iter]
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 6b5d5a805f..9c64cf497f 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -27,6 +27,7 @@
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_MAC_ADDRESS: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
def expand_range(range_str: str) -> list[int]:
--
2.45.2
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-07-30 13:34 ` [PATCH v2 1/1] dts: add text parser for testpmd verbose output jspewock
@ 2024-07-30 15:41 ` Nicholas Pratte
2024-07-30 21:30 ` Jeremy Spewock
2024-07-30 21:33 ` Jeremy Spewock
2024-08-01 8:41 ` Luca Vizzarro
2 siblings, 1 reply; 39+ messages in thread
From: Nicholas Pratte @ 2024-07-30 15:41 UTC (permalink / raw)
To: jspewock
Cc: yoan.picchi, probb, paul.szczepanek, thomas,
Honnappa.Nagarahalli, Luca.Vizzarro, juraj.linkes,
wathsala.vithanage, dev
Good work! I left some probing/clarifying comments below for you.
<snip>
> +@dataclass
> +class TestPmdVerboseOutput(TextParser):
> + """Verbose output generated by Testpmd.
> +
> + This class is the top level of the output, containing verbose output delimited by
> + "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"))
> + #: List of packets handed by the port/queue in this section.
> + packets: list[TestPmdVerbosePacket] = field(
> + metadata=TextParser.wrap(
> + TextParser.find_all(r"(src=[\w\s=:-]+ol_flags: [\w ]+)"),
> + lambda matches_arr: list(map(TestPmdVerbosePacket.parse, matches_arr)),
> + )
> + )
> +
> +
> class TestPmdShell(DPDKShell):
> """Testpmd interactive shell.
>
> @@ -645,7 +767,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 +778,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 +790,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
<snip>
>
> + @staticmethod
> + def extract_verbose_output(output: str) -> list[TestPmdVerboseOutput]:
> + """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`.
> + """
> + iter = re.finditer(r"(port \d+/queue \d+:.*?(?=port \d+/queue \d+|$))", output, re.S)
> + return [TestPmdVerboseOutput.parse(s.group(0)) for s in iter]
> +
I'm trying to think of circumstances where we as developers would be
looking for anything else besides verbose output from the stop method.
Running the command outputs some statistics, but information like port
stats is displayed after the stop command is initiated. I'm
implementing this system right now for one of my test suites, and I'm
wondering if there might be any feasible way to extract output without
needing to input any explicit outputs into this method. I'm putting
output = testpmd.stop() and then calling this method. It looks
something like this:
verbose_output = testpmd.extract_verbose_output(testpmd.stop())
This is easy enough, but it might be a bit confusing for someone new
to the framework. The way that output is gathered is still elusive to
me, and I'm guessing that any commands run in-between setting verbose
mode and when you stop testpmd might influence how output is
generated. But in my experience so far, any statistics or information
I need is gathered before packets are sent, and the need for verbose
output in test cases is one after the other (i send a packet, look at
verbose output, and then move onto the next packet).
<snip>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-07-30 15:41 ` Nicholas Pratte
@ 2024-07-30 21:30 ` Jeremy Spewock
2024-08-02 14:54 ` Nicholas Pratte
0 siblings, 1 reply; 39+ messages in thread
From: Jeremy Spewock @ 2024-07-30 21:30 UTC (permalink / raw)
To: Nicholas Pratte
Cc: yoan.picchi, probb, paul.szczepanek, thomas,
Honnappa.Nagarahalli, Luca.Vizzarro, juraj.linkes,
wathsala.vithanage, dev
On Tue, Jul 30, 2024 at 11:41 AM Nicholas Pratte <npratte@iol.unh.edu> wrote:
>
> Good work! I left some probing/clarifying comments below for you.
>
> <snip>
> > +@dataclass
> > +class TestPmdVerboseOutput(TextParser):
> > + """Verbose output generated by Testpmd.
> > +
> > + This class is the top level of the output, containing verbose output delimited by
> > + "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"))
> > + #: List of packets handed by the port/queue in this section.
> > + packets: list[TestPmdVerbosePacket] = field(
> > + metadata=TextParser.wrap(
> > + TextParser.find_all(r"(src=[\w\s=:-]+ol_flags: [\w ]+)"),
> > + lambda matches_arr: list(map(TestPmdVerbosePacket.parse, matches_arr)),
> > + )
> > + )
> > +
> > +
> > class TestPmdShell(DPDKShell):
> > """Testpmd interactive shell.
> >
> > @@ -645,7 +767,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 +778,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 +790,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
> <snip>
> >
> > + @staticmethod
> > + def extract_verbose_output(output: str) -> list[TestPmdVerboseOutput]:
> > + """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`.
> > + """
> > + iter = re.finditer(r"(port \d+/queue \d+:.*?(?=port \d+/queue \d+|$))", output, re.S)
> > + return [TestPmdVerboseOutput.parse(s.group(0)) for s in iter]
> > +
>
> I'm trying to think of circumstances where we as developers would be
> looking for anything else besides verbose output from the stop method.
> Running the command outputs some statistics, but information like port
> stats is displayed after the stop command is initiated. I'm
> implementing this system right now for one of my test suites, and I'm
> wondering if there might be any feasible way to extract output without
> needing to input any explicit outputs into this method. I'm putting
> output = testpmd.stop() and then calling this method. It looks
> something like this:
>
> verbose_output = testpmd.extract_verbose_output(testpmd.stop())
>
> This is easy enough, but it might be a bit confusing for someone new
> to the framework. The way that output is gathered is still elusive to
> me, and I'm guessing that any commands run in-between setting verbose
> mode and when you stop testpmd might influence how output is
> generated. But in my experience so far, any statistics or information
> I need is gathered before packets are sent, and the need for verbose
> output in test cases is one after the other (i send a packet, look at
> verbose output, and then move onto the next packet).
You're right that in most cases it would come from the stop output,
but the output from that stop command has one other thing as well that
I would consider valuable which is statistics of packets handled by
ports specifically for the duration of the packet forwarding you are
stopping. It is also true that sending other testpmd commands while
verbose output is being sent will change what is collected, so I
didn't want to tie the method specifically to the stop command since
if you did a start command then checked port statistics for example,
it would consume all of the verbose output up until the command to
check port statistics.
For the reason stated above I think it actually might make sense to
make it so that every testpmd method (including ones that currently
return dataclasses) return their original, unmodified collected output
from the testpmd shell as well. Things like port stats can return both
in a tuple potentially. This way if there is asynchronous output like
there is with verbose output other commands don't completely remove
it.
> <snip>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-07-30 13:34 ` [PATCH v2 1/1] dts: add text parser for testpmd verbose output jspewock
2024-07-30 15:41 ` Nicholas Pratte
@ 2024-07-30 21:33 ` Jeremy Spewock
2024-08-01 8:43 ` Luca Vizzarro
2024-08-01 8:41 ` Luca Vizzarro
2 siblings, 1 reply; 39+ messages in thread
From: Jeremy Spewock @ 2024-07-30 21:33 UTC (permalink / raw)
To: yoan.picchi, probb, paul.szczepanek, npratte, thomas,
Honnappa.Nagarahalli, Luca.Vizzarro, juraj.linkes,
wathsala.vithanage
Cc: dev
On Tue, Jul 30, 2024 at 9:37 AM <jspewock@iol.unh.edu> wrote:
<snip>
>
> class TestPmdDevice:
> @@ -577,6 +577,128 @@ class TestPmdPortStats(TextParser):
> tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
>
>
> +class VerboseOLFlag(Flag):
> + """Flag representing the OL flags of a packet from Testpmd verbose output."""
> +
> + #:
> + RTE_MBUF_F_RX_RSS_HASH = auto()
> +
> + #:
> + RTE_MBUF_F_RX_L4_CKSUM_GOOD = auto()
> + #:
> + RTE_MBUF_F_RX_L4_CKSUM_BAD = auto()
> + #:
> + RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = auto()
> +
> + #:
> + RTE_MBUF_F_RX_IP_CKSUM_GOOD = auto()
> + #:
> + RTE_MBUF_F_RX_IP_CKSUM_BAD = auto()
> + #:
> + RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = auto()
> +
> + #:
> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = auto()
> + #:
> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = auto()
> + #:
> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = auto()
> +
After reading more of the API and using this patch to write a test
suite, I believe there is more expansion of these OL flags that should
take place. For starters, there are the Tx OL flags that, while not
seeming to be very useful for the current test suites we are writing,
wouldn't hurt to also include as they seem to be fairly different.
Additionally, there are some other less common RX OL flags that should
be included here just to cover all options. I will work on adding
these into the next version.
<snip>
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-07-30 13:34 ` [PATCH v2 1/1] dts: add text parser for testpmd verbose output jspewock
2024-07-30 15:41 ` Nicholas Pratte
2024-07-30 21:33 ` Jeremy Spewock
@ 2024-08-01 8:41 ` Luca Vizzarro
2024-08-02 13:35 ` Jeremy Spewock
2 siblings, 1 reply; 39+ messages in thread
From: Luca Vizzarro @ 2024-08-01 8:41 UTC (permalink / raw)
To: jspewock, yoan.picchi, probb, paul.szczepanek, npratte, thomas,
Honnappa.Nagarahalli, juraj.linkes, wathsala.vithanage
Cc: dev
Great work Jeremy! Just a couple of minor passable improvement points.
On 30/07/2024 14:34, jspewock@iol.unh.edu wrote:
> +@dataclass
> +class TestPmdVerbosePacket(TextParser):
> + """Packet information provided by verbose output in Testpmd.
> +
> + The "receive/sent queue" information is not included in this dataclass because this value is
> + captured on the outer layer of input found in :class:`TestPmdVerboseOutput`.
> + """
> +
> + #:
> + src_mac: str = field(metadata=TextParser.find(r"src=({})".format(REGEX_FOR_MAC_ADDRESS)))
Just a(n optional) nit: TextParser.find(f"src=({REGEX_FOR_MAC_ADDRESS})")
The raw string is only needed to prevent escaping, which we don't do here.
> + #:
> + dst_mac: str = field(metadata=TextParser.find(r"dst=({})".format(REGEX_FOR_MAC_ADDRESS)))
As above.
> + #: 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]+)"))
> + #:
<snip>
> + @staticmethod
> + def extract_verbose_output(output: str) -> list[TestPmdVerboseOutput]:
> + """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`.
> + """
> + iter = re.finditer(r"(port \d+/queue \d+:.*?(?=port \d+/queue \d+|$))", output, re.S)
How about using a regex that matches what you described? ;) Keeping re.S:
(port \d+/queue \d+.+?ol_flags: [\w ]+)
Would spare you from using complex lookahead constructs and 4.6x less
steps. Maybe it doesn't work with every scenario? Looks like it works
well with the sample output I have. Let me know if it works for you.
Best,
Luca
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-07-30 21:33 ` Jeremy Spewock
@ 2024-08-01 8:43 ` Luca Vizzarro
2024-08-02 13:40 ` Jeremy Spewock
0 siblings, 1 reply; 39+ messages in thread
From: Luca Vizzarro @ 2024-08-01 8:43 UTC (permalink / raw)
To: Jeremy Spewock, yoan.picchi, probb, paul.szczepanek, npratte,
thomas, Honnappa.Nagarahalli, juraj.linkes, wathsala.vithanage
Cc: dev
On 30/07/2024 22:33, Jeremy Spewock wrote:
> On Tue, Jul 30, 2024 at 9:37 AM <jspewock@iol.unh.edu> wrote:
> <snip>
>> +class VerboseOLFlag(Flag):
>> + """Flag representing the OL flags of a packet from Testpmd verbose output."""
>> +
>> + #:
>> + RTE_MBUF_F_RX_RSS_HASH = auto()
>> +
>> + #:
>> + RTE_MBUF_F_RX_L4_CKSUM_GOOD = auto()
>> + #:
>> + RTE_MBUF_F_RX_L4_CKSUM_BAD = auto()
>> + #:
>> + RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = auto()
>> +
>> + #:
>> + RTE_MBUF_F_RX_IP_CKSUM_GOOD = auto()
>> + #:
>> + RTE_MBUF_F_RX_IP_CKSUM_BAD = auto()
>> + #:
>> + RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = auto()
>> +
>> + #:
>> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = auto()
>> + #:
>> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = auto()
>> + #:
>> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = auto()
>> +
> After reading more of the API and using this patch to write a test
> suite, I believe there is more expansion of these OL flags that should
> take place. For starters, there are the Tx OL flags that, while not
> seeming to be very useful for the current test suites we are writing,
> wouldn't hurt to also include as they seem to be fairly different.
> Additionally, there are some other less common RX OL flags that should
> be included here just to cover all options. I will work on adding
> these into the next version.
If you wanted to cover even more, hw_ptype and sw_ptype look like they
could use a data structure. I reckon a flag like the above would also work.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-08-01 8:41 ` Luca Vizzarro
@ 2024-08-02 13:35 ` Jeremy Spewock
0 siblings, 0 replies; 39+ messages in thread
From: Jeremy Spewock @ 2024-08-02 13:35 UTC (permalink / raw)
To: Luca Vizzarro
Cc: yoan.picchi, probb, paul.szczepanek, npratte, thomas,
Honnappa.Nagarahalli, juraj.linkes, wathsala.vithanage, dev
On Thu, Aug 1, 2024 at 4:41 AM Luca Vizzarro <Luca.Vizzarro@arm.com> wrote:
>
> Great work Jeremy! Just a couple of minor passable improvement points.
>
> On 30/07/2024 14:34, jspewock@iol.unh.edu wrote:
>
> > +@dataclass
> > +class TestPmdVerbosePacket(TextParser):
> > + """Packet information provided by verbose output in Testpmd.
> > +
> > + The "receive/sent queue" information is not included in this dataclass because this value is
> > + captured on the outer layer of input found in :class:`TestPmdVerboseOutput`.
> > + """
> > +
> > + #:
> > + src_mac: str = field(metadata=TextParser.find(r"src=({})".format(REGEX_FOR_MAC_ADDRESS)))
> Just a(n optional) nit: TextParser.find(f"src=({REGEX_FOR_MAC_ADDRESS})")
> The raw string is only needed to prevent escaping, which we don't do here.
Ack. I really just left it this way because it also adjusts
highlighting in some IDEs, but there isn't much to see here anyway.
> > + #:
> > + dst_mac: str = field(metadata=TextParser.find(r"dst=({})".format(REGEX_FOR_MAC_ADDRESS)))
> As above.
Ack.
> > + #: 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]+)"))
> > + #:
>
> <snip>
>
> > + @staticmethod
> > + def extract_verbose_output(output: str) -> list[TestPmdVerboseOutput]:
> > + """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`.
> > + """
> > + iter = re.finditer(r"(port \d+/queue \d+:.*?(?=port \d+/queue \d+|$))", output, re.S)
>
> How about using a regex that matches what you described? ;) Keeping re.S:
>
> (port \d+/queue \d+.+?ol_flags: [\w ]+)
>
> Would spare you from using complex lookahead constructs and 4.6x less
> steps. Maybe it doesn't work with every scenario? Looks like it works
> well with the sample output I have. Let me know if it works for you.
>
I tried using something like this actually which is probably why the
docstring reflects that type of language, but I didn't use it because
it doesn't match one specific case. I'm not sure how common it is, and
I haven't seen it happen in my recent testing, but since the verbose
output specifically states that it sent/received X number of packets,
I presume there is a case where that number will be more than 1, and
there will be more than one set of packet information after that first
line. I think I've seen it happen in the past, but I couldn't recreate
it in testing.
For context to this next section, if it wasn't clear, I consider the
`port \d+/queue \d+` line to be the header line and the start of a
"block" of verbose output.
Basically though the problem with this is that if there are more than
one packet under that header line, the lazy approach will only consume
up until the ol_flags of the first packet of a block, and the greedy
approach consumes everything until the last packet of the entire
output. You could use the lazy approach with the next port/queue line
as your delimiter, but then the opening line of the next block of
output is included in the previous block's group. The only way I could
find to get around this and go with the idea of "take everything from
the start of this group until another group starts" but without
capturing the opening of the next block was a look ahead. Again
though, I definitely don't love the regex that I wrote and would love
to find a better alternative.
> Best,
> Luca
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-08-01 8:43 ` Luca Vizzarro
@ 2024-08-02 13:40 ` Jeremy Spewock
0 siblings, 0 replies; 39+ messages in thread
From: Jeremy Spewock @ 2024-08-02 13:40 UTC (permalink / raw)
To: Luca Vizzarro
Cc: yoan.picchi, probb, paul.szczepanek, npratte, thomas,
Honnappa.Nagarahalli, juraj.linkes, wathsala.vithanage, dev
On Thu, Aug 1, 2024 at 4:43 AM Luca Vizzarro <Luca.Vizzarro@arm.com> wrote:
>
> On 30/07/2024 22:33, Jeremy Spewock wrote:
> > On Tue, Jul 30, 2024 at 9:37 AM <jspewock@iol.unh.edu> wrote:
> > <snip>
> >> +class VerboseOLFlag(Flag):
> >> + """Flag representing the OL flags of a packet from Testpmd verbose output."""
> >> +
> >> + #:
> >> + RTE_MBUF_F_RX_RSS_HASH = auto()
> >> +
> >> + #:
> >> + RTE_MBUF_F_RX_L4_CKSUM_GOOD = auto()
> >> + #:
> >> + RTE_MBUF_F_RX_L4_CKSUM_BAD = auto()
> >> + #:
> >> + RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = auto()
> >> +
> >> + #:
> >> + RTE_MBUF_F_RX_IP_CKSUM_GOOD = auto()
> >> + #:
> >> + RTE_MBUF_F_RX_IP_CKSUM_BAD = auto()
> >> + #:
> >> + RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = auto()
> >> +
> >> + #:
> >> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = auto()
> >> + #:
> >> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = auto()
> >> + #:
> >> + RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = auto()
> >> +
> > After reading more of the API and using this patch to write a test
> > suite, I believe there is more expansion of these OL flags that should
> > take place. For starters, there are the Tx OL flags that, while not
> > seeming to be very useful for the current test suites we are writing,
> > wouldn't hurt to also include as they seem to be fairly different.
> > Additionally, there are some other less common RX OL flags that should
> > be included here just to cover all options. I will work on adding
> > these into the next version.
>
> If you wanted to cover even more, hw_ptype and sw_ptype look like they
> could use a data structure. I reckon a flag like the above would also work.
Ack, it's probably worth making those a little more structured as well.
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-07-30 21:30 ` Jeremy Spewock
@ 2024-08-02 14:54 ` Nicholas Pratte
2024-08-02 17:38 ` Jeremy Spewock
0 siblings, 1 reply; 39+ messages in thread
From: Nicholas Pratte @ 2024-08-02 14:54 UTC (permalink / raw)
To: Jeremy Spewock
Cc: yoan.picchi, probb, paul.szczepanek, thomas,
Honnappa.Nagarahalli, Luca.Vizzarro, juraj.linkes,
wathsala.vithanage, dev
<snip>
> You're right that in most cases it would come from the stop output,
> but the output from that stop command has one other thing as well that
> I would consider valuable which is statistics of packets handled by
> ports specifically for the duration of the packet forwarding you are
> stopping. It is also true that sending other testpmd commands while
> verbose output is being sent will change what is collected, so I
> didn't want to tie the method specifically to the stop command since
> if you did a start command then checked port statistics for example,
> it would consume all of the verbose output up until the command to
> check port statistics.
>
> For the reason stated above I think it actually might make sense to
> make it so that every testpmd method (including ones that currently
> return dataclasses) return their original, unmodified collected output
> from the testpmd shell as well. Things like port stats can return both
> in a tuple potentially. This way if there is asynchronous output like
> there is with verbose output other commands don't completely remove
> it.
I agree! I think giving each testpmd method its own output would add
more consistency. An idea I had floating around that kind of relates
to your suggestion above was introducing some instance variables that
could enable the testpmd shell object to be smart enough to
automatically scan, and keep a record of, any verbose output that
comes out across any command run. The TestPMDShell class could track
whether verbose mode is on or not, and if True, run additional logic
to scan for verbose output and add it to a data structure for access
every time a command is run. Then users, from the perspective of
writing a test suite, could do something like 'output in
testpmd.verbose_output', where verbose_output is an instance data
structure of the TestPMDShell. This might be overcomplicated to
implement, but it was an idea I had that might make using verbose mode
more streamlined. What are your thoughts?
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-08-02 14:54 ` Nicholas Pratte
@ 2024-08-02 17:38 ` Jeremy Spewock
2024-08-05 13:20 ` Nicholas Pratte
0 siblings, 1 reply; 39+ messages in thread
From: Jeremy Spewock @ 2024-08-02 17:38 UTC (permalink / raw)
To: Nicholas Pratte
Cc: yoan.picchi, probb, paul.szczepanek, thomas,
Honnappa.Nagarahalli, Luca.Vizzarro, juraj.linkes,
wathsala.vithanage, dev
On Fri, Aug 2, 2024 at 10:54 AM Nicholas Pratte <npratte@iol.unh.edu> wrote:
>
> <snip>
> > You're right that in most cases it would come from the stop output,
> > but the output from that stop command has one other thing as well that
> > I would consider valuable which is statistics of packets handled by
> > ports specifically for the duration of the packet forwarding you are
> > stopping. It is also true that sending other testpmd commands while
> > verbose output is being sent will change what is collected, so I
> > didn't want to tie the method specifically to the stop command since
> > if you did a start command then checked port statistics for example,
> > it would consume all of the verbose output up until the command to
> > check port statistics.
> >
> > For the reason stated above I think it actually might make sense to
> > make it so that every testpmd method (including ones that currently
> > return dataclasses) return their original, unmodified collected output
> > from the testpmd shell as well. Things like port stats can return both
> > in a tuple potentially. This way if there is asynchronous output like
> > there is with verbose output other commands don't completely remove
> > it.
>
> I agree! I think giving each testpmd method its own output would add
> more consistency. An idea I had floating around that kind of relates
> to your suggestion above was introducing some instance variables that
> could enable the testpmd shell object to be smart enough to
> automatically scan, and keep a record of, any verbose output that
> comes out across any command run. The TestPMDShell class could track
> whether verbose mode is on or not, and if True, run additional logic
> to scan for verbose output and add it to a data structure for access
> every time a command is run. Then users, from the perspective of
> writing a test suite, could do something like 'output in
> testpmd.verbose_output', where verbose_output is an instance data
> structure of the TestPMDShell. This might be overcomplicated to
> implement, but it was an idea I had that might make using verbose mode
> more streamlined. What are your thoughts?
I like the sound of this idea a lot actually since it would remove the
chance of the output just completely being thrown away. In my own test
suite I managed to dance around this by strategically placing my
testpmd commands, but this could save people some headache in the
future. I feel like this wouldn't be something overly complicated to
implement either, all we would have to do is extend the send_command
method in the TestpmdShell class and check a boolean for if verbose is
on, extract this output. If/how to clear this list would be something
to think about, but I would say that, in general, the idea of making
sure we don't lose information is something that I'm all for.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v2 1/1] dts: add text parser for testpmd verbose output
2024-08-02 17:38 ` Jeremy Spewock
@ 2024-08-05 13:20 ` Nicholas Pratte
0 siblings, 0 replies; 39+ messages in thread
From: Nicholas Pratte @ 2024-08-05 13:20 UTC (permalink / raw)
To: Jeremy Spewock
Cc: yoan.picchi, probb, paul.szczepanek, thomas,
Honnappa.Nagarahalli, Luca.Vizzarro, juraj.linkes,
wathsala.vithanage, dev
> I like the sound of this idea a lot actually since it would remove the
> chance of the output just completely being thrown away. In my own test
> suite I managed to dance around this by strategically placing my
> testpmd commands, but this could save people some headache in the
> future. I feel like this wouldn't be something overly complicated to
> implement either, all we would have to do is extend the send_command
> method in the TestpmdShell class and check a boolean for if verbose is
> on, extract this output. If/how to clear this list would be something
> to think about, but I would say that, in general, the idea of making
> sure we don't lose information is something that I'm all for.
That's a good point that you could just modify the send_command
method. In my head I was thinking that we'd have to modify each
individual method! Totally forget that all those testpmd methods I was
thinking about stem from send_command().
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v3 0/1] dts: testpmd verbose parser
2024-07-29 20:39 [PATCH v1 0/1] dts: testpmd verbose parser jspewock
2024-07-29 20:39 ` [PATCH v1 1/1] dts: add text parser for testpmd verbose output jspewock
2024-07-30 13:34 ` [PATCH v2 0/1] dts: testpmd verbose parser jspewock
@ 2024-08-08 20:36 ` jspewock
2024-08-08 20:36 ` [PATCH v3 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-18 16:34 ` [PATCH v4 0/1] dts: testpmd verbose parser jspewock
` (3 subsequent siblings)
6 siblings, 1 reply; 39+ messages in thread
From: jspewock @ 2024-08-08 20:36 UTC (permalink / raw)
To: thomas, yoan.picchi, paul.szczepanek, Honnappa.Nagarahalli,
probb, wathsala.vithanage, Luca.Vizzarro, juraj.linkes, npratte,
alex.chapman
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
I have not yet explored the idea of caching verbose output from all
commands rather than simply parsing whatever output is passed into the
method, but I wanted to get what I have currently out in the meantime.
v3:
* Add more OL flags to include everything listed in rte_mbuf_core.h
* Address comments in the previous version regarding formatting and
adding a new class for packet types in verbose output
* Changed the structure of verbose output gathering so that rather
a block of verbose output that contains a list of packets in the
burst it is just simply a list of packet information. This is done
by taking the header information from the start of the burst and
caching it to add to all packet info.
Jeremy Spewock (1):
dts: add text parser for testpmd verbose output
dts/framework/parser.py | 30 ++
dts/framework/remote_session/testpmd_shell.py | 405 +++++++++++++++++-
dts/framework/utils.py | 1 +
3 files changed, 434 insertions(+), 2 deletions(-)
--
2.45.2
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v3 1/1] dts: add text parser for testpmd verbose output
2024-08-08 20:36 ` [PATCH v3 0/1] dts: testpmd verbose parser jspewock
@ 2024-08-08 20:36 ` jspewock
2024-08-08 21:49 ` Jeremy Spewock
2024-09-09 11:44 ` Juraj Linkeš
0 siblings, 2 replies; 39+ messages in thread
From: jspewock @ 2024-08-08 20:36 UTC (permalink / raw)
To: thomas, yoan.picchi, paul.szczepanek, Honnappa.Nagarahalli,
probb, wathsala.vithanage, Luca.Vizzarro, juraj.linkes, npratte,
alex.chapman
Cc: dev, Jeremy Spewock
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/parser.py | 30 ++
dts/framework/remote_session/testpmd_shell.py | 405 +++++++++++++++++-
dts/framework/utils.py | 1 +
3 files changed, 434 insertions(+), 2 deletions(-)
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 741dfff821..0b39025a48 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -160,6 +160,36 @@ def _find(text: str) -> Any:
return ParserFn(TextParser_fn=_find)
+ @staticmethod
+ def find_all(
+ pattern: str | re.Pattern[str],
+ flags: re.RegexFlag = re.RegexFlag(0),
+ ) -> ParserFn:
+ """Makes a parser function that finds all of the regular expression matches in the text.
+
+ If there are no matches found in the text than None will be returned, otherwise a list
+ containing all matches will be returned. Patterns that contain multiple groups will pack
+ the matches for each group into a tuple.
+
+ Args:
+ pattern: The regular expression pattern.
+ flags: The regular expression flags. Ignored if the given pattern is already compiled.
+
+ Returns:
+ A :class:`ParserFn` that can be used as metadata for a dataclass field.
+ """
+ if isinstance(pattern, str):
+ pattern = re.compile(pattern, flags)
+
+ def _find_all(text: str) -> list[str] | None:
+ m = pattern.findall(text)
+ if len(m) == 0:
+ return None
+
+ return m
+
+ return ParserFn(TextParser_fn=_find_all)
+
@staticmethod
def find_int(
pattern: str | re.Pattern[str],
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 43e9f56517..7d0b5a374c 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -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,377 @@ class TestPmdPortStats(TextParser):
tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
+class OLFlag(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.
+ """
+
+ # RX flags
+ #:
+ RTE_MBUF_F_RX_RSS_HASH = auto()
+
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = auto()
+ #:
+ RTE_MBUF_F_RX_L4_CKSUM_NONE = auto()
+
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN = auto()
+ #:
+ RTE_MBUF_F_RX_IP_CKSUM_NONE = auto()
+
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_GOOD = auto()
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = auto()
+ #:
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_INVALID = auto()
+
+ #:
+ RTE_MBUF_F_RX_VLAN = auto()
+ #:
+ RTE_MBUF_F_RX_FDIR = auto()
+ #:
+ RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD = auto()
+ #:
+ RTE_MBUF_F_RX_VLAN_STRIPPED = auto()
+ #: RX IEEE1588 L2 Ethernet PT Packet.
+ RTE_MBUF_F_RX_IEEE1588_PTP = auto()
+ #: RX IEEE1588 L2/L4 timestamped packet.
+ RTE_MBUF_F_RX_IEEE1588_TMST = auto()
+ #: FD id reported if FDIR match.
+ RTE_MBUF_F_RX_FDIR_ID = auto()
+ #: Flexible bytes reported if FDIR match.
+ RTE_MBUF_F_RX_FDIR_FLX = auto()
+ #:
+ RTE_MBUF_F_RX_QINQ_STRIPPED = auto()
+ #:
+ RTE_MBUF_F_RX_LRO = auto()
+ #:
+ RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED = auto()
+ #:
+ RTE_MBUF_F_RX_QINQ = auto()
+
+ # TX flags
+ #:
+ RTE_MBUF_F_TX_OUTER_UDP_CKSUM = auto()
+ #:
+ RTE_MBUF_F_TX_UDP_SEG = auto()
+ #:
+ RTE_MBUF_F_TX_SEC_OFFLOAD = auto()
+ #:
+ RTE_MBUF_F_TX_MACSEC = auto()
+
+ #:
+ RTE_MBUF_F_TX_TUNNEL_VXLAN = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GRE = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_IPIP = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GENEVE = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_MPLSINUDP = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GTP = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_ESP = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_IP = auto()
+ #:
+ RTE_MBUF_F_TX_TUNNEL_UDP = auto()
+
+ #:
+ RTE_MBUF_F_TX_QINQ = auto()
+ #:
+ RTE_MBUF_F_TX_TCP_SEG = auto()
+ #: TX IEEE1588 packet to timestamp.
+ RTE_MBUF_F_TX_IEEE1588_TMST = auto()
+
+ #:
+ RTE_MBUF_F_TX_L4_NO_CKSUM = auto()
+ #:
+ RTE_MBUF_F_TX_TCP_CKSUM = auto()
+ #:
+ RTE_MBUF_F_TX_SCTP_CKSUM = auto()
+ #:
+ RTE_MBUF_F_TX_UDP_CKSUM = auto()
+ #:
+ RTE_MBUF_F_TX_L4_MASK = auto()
+ #:
+ RTE_MBUF_F_TX_IP_CKSUM = auto()
+ #:
+ RTE_MBUF_F_TX_OUTER_IP_CKSUM = auto()
+
+ #:
+ RTE_MBUF_F_TX_IPV4 = auto()
+ #:
+ RTE_MBUF_F_TX_IPV6 = auto()
+ #:
+ RTE_MBUF_F_TX_VLAN = auto()
+ #:
+ RTE_MBUF_F_TX_OUTER_IPV4 = auto()
+ #:
+ 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 name in cls.__members__:
+ 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."""
+
+ # L2
+ #:
+ L2_ETHER = auto()
+ #:
+ L2_ETHER_TIMESYNC = auto()
+ #:
+ L2_ETHER_ARP = auto()
+ #:
+ L2_ETHER_LLDP = auto()
+ #:
+ L2_ETHER_NSH = auto()
+ #:
+ L2_ETHER_VLAN = auto()
+ #:
+ L2_ETHER_QINQ = auto()
+ #:
+ L2_ETHER_PPPOE = auto()
+ #:
+ L2_ETHER_FCOE = auto()
+ #:
+ L2_ETHER_MPLS = auto()
+ #:
+ L2_UNKNOWN = auto()
+
+ # L3
+ #:
+ L3_IPV4 = auto()
+ #:
+ L3_IPV4_EXT = auto()
+ #:
+ L3_IPV6 = auto()
+ #:
+ L3_IPV4_EXT_UNKNOWN = auto()
+ #:
+ L3_IPV6_EXT = auto()
+ #:
+ L3_IPV6_EXT_UNKNOWN = auto()
+ #:
+ L3_UNKNOWN = auto()
+
+ # L4
+ #:
+ L4_TCP = auto()
+ #:
+ L4_UDP = auto()
+ #:
+ L4_FRAG = auto()
+ #:
+ L4_SCTP = auto()
+ #:
+ L4_ICMP = auto()
+ #:
+ L4_NONFRAG = auto()
+ #:
+ L4_IGMP = auto()
+ #:
+ L4_UNKNOWN = auto()
+
+ # Tunnel
+ #:
+ TUNNEL_IP = auto()
+ #:
+ TUNNEL_GRE = auto()
+ #:
+ TUNNEL_VXLAN = auto()
+ #:
+ TUNNEL_NVGRE = auto()
+ #:
+ TUNNEL_GENEVE = auto()
+ #:
+ TUNNEL_GRENAT = auto()
+ #:
+ TUNNEL_GTPC = auto()
+ #:
+ TUNNEL_GTPU = auto()
+ #:
+ TUNNEL_ESP = auto()
+ #:
+ TUNNEL_L2TP = auto()
+ #:
+ TUNNEL_VXLAN_GPE = auto()
+ #:
+ TUNNEL_MPLS_IN_UDP = auto()
+ #:
+ TUNNEL_MPLS_IN_GRE = auto()
+ #:
+ TUNNEL_UNKNOWN = auto()
+
+ # Inner L2
+ #:
+ INNER_L2_ETHER = auto()
+ #:
+ INNER_L2_ETHER_VLAN = auto()
+ #:
+ INNER_L2_ETHER_QINQ = auto()
+ #:
+ INNER_L2_UNKNOWN = auto()
+
+ # Inner L3
+ #:
+ INNER_L3_IPV4 = auto()
+ #:
+ INNER_L3_IPV4_EXT = auto()
+ #:
+ INNER_L3_IPV6 = auto()
+ #:
+ INNER_L3_IPV4_EXT_UNKNOWN = auto()
+ #:
+ INNER_L3_IPV6_EXT = auto()
+ #:
+ INNER_L3_IPV6_EXT_UNKNOWN = auto()
+ #:
+ INNER_L3_UNKNOWN = auto()
+
+ # Inner L4
+ #:
+ INNER_L4_TCP = auto()
+ #:
+ INNER_L4_UDP = auto()
+ #:
+ INNER_L4_FRAG = auto()
+ #:
+ INNER_L4_SCTP = auto()
+ #:
+ INNER_L4_ICMP = auto()
+ #:
+ INNER_L4_NONFRAG = auto()
+ #:
+ 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 ol_flag values.
+
+ Returns:
+ A new instance of the flag.
+ """
+ flag = cls(0)
+ for name in arr:
+ if name in cls.__members__:
+ 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: OLFlag = field(metadata=OLFlag.make_parser())
+ #: RSS has 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 +1016,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 +1027,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 +1039,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 +1181,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 \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()
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 6b5d5a805f..9c64cf497f 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -27,6 +27,7 @@
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_MAC_ADDRESS: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
def expand_range(range_str: str) -> list[int]:
--
2.45.2
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 1/1] dts: add text parser for testpmd verbose output
2024-08-08 20:36 ` [PATCH v3 1/1] dts: add text parser for testpmd verbose output jspewock
@ 2024-08-08 21:49 ` Jeremy Spewock
2024-08-12 17:32 ` Nicholas Pratte
2024-09-09 11:44 ` Juraj Linkeš
1 sibling, 1 reply; 39+ messages in thread
From: Jeremy Spewock @ 2024-08-08 21:49 UTC (permalink / raw)
To: thomas, yoan.picchi, paul.szczepanek, Honnappa.Nagarahalli,
probb, wathsala.vithanage, Luca.Vizzarro, juraj.linkes, npratte,
alex.chapman
Cc: dev
On Thu, Aug 8, 2024 at 4:36 PM <jspewock@iol.unh.edu> wrote:
>
> 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/parser.py | 30 ++
> dts/framework/remote_session/testpmd_shell.py | 405 +++++++++++++++++-
> dts/framework/utils.py | 1 +
> 3 files changed, 434 insertions(+), 2 deletions(-)
>
> diff --git a/dts/framework/parser.py b/dts/framework/parser.py
> index 741dfff821..0b39025a48 100644
> --- a/dts/framework/parser.py
> +++ b/dts/framework/parser.py
> @@ -160,6 +160,36 @@ def _find(text: str) -> Any:
>
> return ParserFn(TextParser_fn=_find)
>
> + @staticmethod
> + def find_all(
I just realized that I forgot to take this method out since it is no
longer used in this patch now that I removed the idea of a block of
output that contains a burst of packets. The method could still be
useful in the future, but it isn't used anywhere now, so I can remove
it in the next version if no one sees a use for it. I'm also open to
leaving it there for the "just in case" it is needed in the future.
What do you all think?
> + pattern: str | re.Pattern[str],
> + flags: re.RegexFlag = re.RegexFlag(0),
> + ) -> ParserFn:
> + """Makes a parser function that finds all of the regular expression matches in the text.
> +
> + If there are no matches found in the text than None will be returned, otherwise a list
> + containing all matches will be returned. Patterns that contain multiple groups will pack
> + the matches for each group into a tuple.
> +
> + Args:
> + pattern: The regular expression pattern.
> + flags: The regular expression flags. Ignored if the given pattern is already compiled.
> +
> + Returns:
> + A :class:`ParserFn` that can be used as metadata for a dataclass field.
> + """
> + if isinstance(pattern, str):
> + pattern = re.compile(pattern, flags)
> +
> + def _find_all(text: str) -> list[str] | None:
> + m = pattern.findall(text)
> + if len(m) == 0:
> + return None
> +
> + return m
> +
> + return ParserFn(TextParser_fn=_find_all)
> +
<snip>
> 2.45.2
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 1/1] dts: add text parser for testpmd verbose output
2024-08-08 21:49 ` Jeremy Spewock
@ 2024-08-12 17:32 ` Nicholas Pratte
0 siblings, 0 replies; 39+ messages in thread
From: Nicholas Pratte @ 2024-08-12 17:32 UTC (permalink / raw)
To: Jeremy Spewock
Cc: thomas, yoan.picchi, paul.szczepanek, Honnappa.Nagarahalli,
probb, wathsala.vithanage, Luca.Vizzarro, juraj.linkes,
alex.chapman, dev
Great work!
I think it'd be fine to just keep if you ask me, I could see this
being used in the future. I'm also looking at it from the perspective
of 'what if i would have to write this myself,' if it turns out that
we need it again for something later. It's easier to remove later if
it turns out we aren't using it, but it'd likely be more
time-consuming to remove it now and implement it again later,
considering that time has already been spent testing and building it.
On Thu, Aug 8, 2024 at 5:49 PM Jeremy Spewock <jspewock@iol.unh.edu> wrote:
>
> On Thu, Aug 8, 2024 at 4:36 PM <jspewock@iol.unh.edu> wrote:
> >
> > 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/parser.py | 30 ++
> > dts/framework/remote_session/testpmd_shell.py | 405 +++++++++++++++++-
> > dts/framework/utils.py | 1 +
> > 3 files changed, 434 insertions(+), 2 deletions(-)
> >
> > diff --git a/dts/framework/parser.py b/dts/framework/parser.py
> > index 741dfff821..0b39025a48 100644
> > --- a/dts/framework/parser.py
> > +++ b/dts/framework/parser.py
> > @@ -160,6 +160,36 @@ def _find(text: str) -> Any:
> >
> > return ParserFn(TextParser_fn=_find)
> >
> > + @staticmethod
> > + def find_all(
>
> I just realized that I forgot to take this method out since it is no
> longer used in this patch now that I removed the idea of a block of
> output that contains a burst of packets. The method could still be
> useful in the future, but it isn't used anywhere now, so I can remove
> it in the next version if no one sees a use for it. I'm also open to
> leaving it there for the "just in case" it is needed in the future.
> What do you all think?
>
> > + pattern: str | re.Pattern[str],
> > + flags: re.RegexFlag = re.RegexFlag(0),
> > + ) -> ParserFn:
> > + """Makes a parser function that finds all of the regular expression matches in the text.
> > +
> > + If there are no matches found in the text than None will be returned, otherwise a list
> > + containing all matches will be returned. Patterns that contain multiple groups will pack
> > + the matches for each group into a tuple.
> > +
> > + Args:
> > + pattern: The regular expression pattern.
> > + flags: The regular expression flags. Ignored if the given pattern is already compiled.
> > +
> > + Returns:
> > + A :class:`ParserFn` that can be used as metadata for a dataclass field.
> > + """
> > + if isinstance(pattern, str):
> > + pattern = re.compile(pattern, flags)
> > +
> > + def _find_all(text: str) -> list[str] | None:
> > + m = pattern.findall(text)
> > + if len(m) == 0:
> > + return None
> > +
> > + return m
> > +
> > + return ParserFn(TextParser_fn=_find_all)
> > +
> <snip>
> > 2.45.2
> >
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 1/1] dts: add text parser for testpmd verbose output
2024-08-08 20:36 ` [PATCH v3 1/1] dts: add text parser for testpmd verbose output jspewock
2024-08-08 21:49 ` Jeremy Spewock
@ 2024-09-09 11:44 ` Juraj Linkeš
2024-09-17 13:40 ` Jeremy Spewock
1 sibling, 1 reply; 39+ messages in thread
From: Juraj Linkeš @ 2024-09-09 11:44 UTC (permalink / raw)
To: jspewock, thomas, yoan.picchi, paul.szczepanek,
Honnappa.Nagarahalli, probb, wathsala.vithanage, Luca.Vizzarro,
npratte, alex.chapman
Cc: dev
> diff --git a/dts/framework/parser.py b/dts/framework/parser.py
> index 741dfff821..0b39025a48 100644
> --- a/dts/framework/parser.py
> +++ b/dts/framework/parser.py
> @@ -160,6 +160,36 @@ def _find(text: str) -> Any:
>
> return ParserFn(TextParser_fn=_find)
>
> + @staticmethod
> + def find_all(
> + pattern: str | re.Pattern[str],
> + flags: re.RegexFlag = re.RegexFlag(0),
> + ) -> ParserFn:
I'd remove this if it's not used, the rule being let's not introduce
unused code because it's not going to be maintained. We can always add
it when needed.
> + """Makes a parser function that finds all of the regular expression matches in the text.
> +
> + If there are no matches found in the text than None will be returned, otherwise a list
then None, but maybe a comma would be better (found in the text, None
will be returned)
> + containing all matches will be returned. Patterns that contain multiple groups will pack
> + the matches for each group into a tuple.
> +
> diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
> index 43e9f56517..7d0b5a374c 100644
> --- a/dts/framework/remote_session/testpmd_shell.py
> +++ b/dts/framework/remote_session/testpmd_shell.py
> @@ -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,377 @@ class TestPmdPortStats(TextParser):
> tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
>
>
> +class OLFlag(Flag):
We should come up with a consistent naming scheme for the various
offloads. In the capabilities patch, I've introduced
RxOffloadCapability. I think we can use the full word Offload and we
should also capture in the name what sort of offload it is. In this
case, would PacketOffloadFlag be a good name?
> + """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.
I like the reference, let's also mention the name of the file
rte_mbuf_core.h. Maybe we should add more references like these to other
flags.
> + """
> +
> + # RX flags
> + #:
> + RTE_MBUF_F_RX_RSS_HASH = auto()
> +
> + #:
I noticed the flags are not sorted the same way as in rte_mbuf_core.h. I
think there's value in using the same flag values.
We could also add descriptions to the flag if there are some to be found
in rte_mbuf_core.h.
> +
> + # TX flags
> + #:
> + RTE_MBUF_F_TX_OUTER_UDP_CKSUM = auto()
Since there is a gap between RX and TX flags, you can just assign the
actual value here (1 << 41) and the continue using 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 name in cls.__members__:
We could also do if cls[name] in cls. It's basically the same thing, but
doesn't use the dunder method.
> + 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."""
Now this docstring doesn't reference from where these come from.
I found these in rte_mbuf_ptype.h, but from what I can tell, they're not
actual flags, just regular numbers:
#define RTE_PTYPE_L2_ETHER 0x00000001
#define RTE_PTYPE_L2_ETHER_TIMESYNC 0x00000002
etc., so we're basically converting that to flags. I think this is OK
and we don't really need to concern ourselves with the actual values,
just the order.
> + @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.
ol_flag looks like a copy-paste.
> +
> + Returns:
> + A new instance of the flag.
> + """
> + flag = cls(0)
> + for name in arr:
> + if name in cls.__members__:
> + 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`
I think there should be a comma before hardware (on the next line).
> + 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):
> + ol_flags: OLFlag = field(metadata=OLFlag.make_parser())
> + #: RSS has of the packet in hex.
typo: hash
> class TestPmdShell(DPDKShell):
> + @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 \d packets)?)\s*"
Looks like sent packets won't be captured by this.
> diff --git a/dts/framework/utils.py b/dts/framework/utils.py
> index 6b5d5a805f..9c64cf497f 100644
> --- a/dts/framework/utils.py
> +++ b/dts/framework/utils.py
> @@ -27,6 +27,7 @@
> 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_MAC_ADDRESS: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
Is this the only format that testpmd returns?
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 1/1] dts: add text parser for testpmd verbose output
2024-09-09 11:44 ` Juraj Linkeš
@ 2024-09-17 13:40 ` Jeremy Spewock
2024-09-18 8:09 ` Juraj Linkeš
0 siblings, 1 reply; 39+ messages in thread
From: Jeremy Spewock @ 2024-09-17 13:40 UTC (permalink / raw)
To: Juraj Linkeš
Cc: thomas, yoan.picchi, paul.szczepanek, Honnappa.Nagarahalli,
probb, wathsala.vithanage, Luca.Vizzarro, npratte, alex.chapman,
dev
On Mon, Sep 9, 2024 at 7:44 AM Juraj Linkeš <juraj.linkes@pantheon.tech> wrote:
>
>
> > diff --git a/dts/framework/parser.py b/dts/framework/parser.py
> > index 741dfff821..0b39025a48 100644
> > --- a/dts/framework/parser.py
> > +++ b/dts/framework/parser.py
> > @@ -160,6 +160,36 @@ def _find(text: str) -> Any:
> >
> > return ParserFn(TextParser_fn=_find)
> >
> > + @staticmethod
> > + def find_all(
> > + pattern: str | re.Pattern[str],
> > + flags: re.RegexFlag = re.RegexFlag(0),
> > + ) -> ParserFn:
>
> I'd remove this if it's not used, the rule being let's not introduce
> unused code because it's not going to be maintained. We can always add
> it when needed.
Since submitting this I did actually find one use for it in the Rx/Tx
suite, but that one has yet to undergo review, so it could be the case
that people don't like that implementation.
>
> > + """Makes a parser function that finds all of the regular expression matches in the text.
> > +
> > + If there are no matches found in the text than None will be returned, otherwise a list
>
> then None, but maybe a comma would be better (found in the text, None
> will be returned)
Ack.
>
> > + containing all matches will be returned. Patterns that contain multiple groups will pack
> > + the matches for each group into a tuple.
> > +
>
> > diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
> > index 43e9f56517..7d0b5a374c 100644
> > --- a/dts/framework/remote_session/testpmd_shell.py
> > +++ b/dts/framework/remote_session/testpmd_shell.py
> > @@ -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,377 @@ class TestPmdPortStats(TextParser):
> > tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
> >
> >
> > +class OLFlag(Flag):
>
> We should come up with a consistent naming scheme for the various
> offloads. In the capabilities patch, I've introduced
> RxOffloadCapability. I think we can use the full word Offload and we
> should also capture in the name what sort of offload it is. In this
> case, would PacketOffloadFlag be a good name?
This is a good point, I was just naming according to what they called
it in the verbose output, but this probably should be more consistent.
I think that PacketOffloadFlag would make sense.
>
> > + """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.
>
> I like the reference, let's also mention the name of the file
> rte_mbuf_core.h. Maybe we should add more references like these to other
> flags.
Ack.
>
> > + """
> > +
> > + # RX flags
> > + #:
> > + RTE_MBUF_F_RX_RSS_HASH = auto()
> > +
> > + #:
>
> I noticed the flags are not sorted the same way as in rte_mbuf_core.h. I
> think there's value in using the same flag values.
>
> We could also add descriptions to the flag if there are some to be found
> in rte_mbuf_core.h.
Yeah, I reordered some to try and keep consistent groupings between
things like checksums or what I thought made logical sense. I figured
that the order wouldn't matter all that much since the verbose output
just uses the flag names, but you're right, there could be some value
in keeping them the same. Especially if we try to write parsers for
the new verbose output modes that are being worked on in testpmd since
one of them is just a hexdump.
>
> > +
> > + # TX flags
> > + #:
> > + RTE_MBUF_F_TX_OUTER_UDP_CKSUM = auto()
>
> Since there is a gap between RX and TX flags, you can just assign the
> actual value here (1 << 41) and the continue using auto().
Good point, I can make that change.
>
>
> > + @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 name in cls.__members__:
>
> We could also do if cls[name] in cls. It's basically the same thing, but
> doesn't use the dunder method.
Ack.
>
> > + 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."""
>
> Now this docstring doesn't reference from where these come from.
>
> I found these in rte_mbuf_ptype.h, but from what I can tell, they're not
> actual flags, just regular numbers:
Right, I did take them from there. Probably good to mention where
these come from as well.
> #define RTE_PTYPE_L2_ETHER 0x00000001
> #define RTE_PTYPE_L2_ETHER_TIMESYNC 0x00000002
>
> etc., so we're basically converting that to flags. I think this is OK
> and we don't really need to concern ourselves with the actual values,
> just the order.
>
Ack.
>
> > + @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.
>
> ol_flag looks like a copy-paste.
Oops, good catch!
>
> > +
> > + Returns:
> > + A new instance of the flag.
> > + """
> > + flag = cls(0)
> > + for name in arr:
> > + if name in cls.__members__:
> > + 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`
>
> I think there should be a comma before hardware (on the next line).
Ack.
>
> > + 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):
>
> > + ol_flags: OLFlag = field(metadata=OLFlag.make_parser())
> > + #: RSS has of the packet in hex.
>
> typo: hash
Ack.
>
>
> > class TestPmdShell(DPDKShell):
>
> > + @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 \d packets)?)\s*"
>
> Looks like sent packets won't be captured by this.
Right, I think I wrote this implementation for received first and then
made it more dynamic so it could support both but missed this. Good
catch!
>
>
> > diff --git a/dts/framework/utils.py b/dts/framework/utils.py
> > index 6b5d5a805f..9c64cf497f 100644
> > --- a/dts/framework/utils.py
> > +++ b/dts/framework/utils.py
> > @@ -27,6 +27,7 @@
> > 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_MAC_ADDRESS: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
>
> Is this the only format that testpmd returns?
I believe so, but because I'm not completely sure I can change this
regex to support other variants as well. The hyphen separated one is
easy enough that it might as well be included, the group of 4
separated by a dot might be a little more involved but I can probably
get it to work.
>
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 1/1] dts: add text parser for testpmd verbose output
2024-09-17 13:40 ` Jeremy Spewock
@ 2024-09-18 8:09 ` Juraj Linkeš
0 siblings, 0 replies; 39+ messages in thread
From: Juraj Linkeš @ 2024-09-18 8:09 UTC (permalink / raw)
To: Jeremy Spewock
Cc: thomas, yoan.picchi, paul.szczepanek, Honnappa.Nagarahalli,
probb, wathsala.vithanage, Luca.Vizzarro, npratte, alex.chapman,
dev
On 17. 9. 2024 15:40, Jeremy Spewock wrote:
> On Mon, Sep 9, 2024 at 7:44 AM Juraj Linkeš <juraj.linkes@pantheon.tech> wrote:
>>
>>
>>> diff --git a/dts/framework/parser.py b/dts/framework/parser.py
>>> index 741dfff821..0b39025a48 100644
>>> --- a/dts/framework/parser.py
>>> +++ b/dts/framework/parser.py
>>> @@ -160,6 +160,36 @@ def _find(text: str) -> Any:
>>>
>>> return ParserFn(TextParser_fn=_find)
>>>
>>> + @staticmethod
>>> + def find_all(
>>> + pattern: str | re.Pattern[str],
>>> + flags: re.RegexFlag = re.RegexFlag(0),
>>> + ) -> ParserFn:
>>
>> I'd remove this if it's not used, the rule being let's not introduce
>> unused code because it's not going to be maintained. We can always add
>> it when needed.
>
> Since submitting this I did actually find one use for it in the Rx/Tx
> suite, but that one has yet to undergo review, so it could be the case
> that people don't like that implementation.
>
Ok, we can (and probably should) add it in that test suite patchset if
needed. The required infrastructure is in this patchset and additions to
it can be added in individual test suites (if only that one uses that
addition).
>>> diff --git a/dts/framework/utils.py b/dts/framework/utils.py
>>> index 6b5d5a805f..9c64cf497f 100644
>>> --- a/dts/framework/utils.py
>>> +++ b/dts/framework/utils.py
>>> @@ -27,6 +27,7 @@
>>> 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_MAC_ADDRESS: str = r"(?:[\da-fA-F]{2}:){5}[\da-fA-F]{2}"
>>
>> Is this the only format that testpmd returns?
>
> I believe so, but because I'm not completely sure I can change this
> regex to support other variants as well. The hyphen separated one is
> easy enough that it might as well be included, the group of 4
> separated by a dot might be a little more involved but I can probably
> get it to work.
>
Ok, might as well be safe, doesn't sound like a big change.
A small point is to make the regex as readable as possible - we could
split it into multiple regexes (and try to match multiple times) or put
the one big regex string on mutliple lines if possible (with each line
being a separate variant or multiple closely related variants).
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v4 0/1] dts: testpmd verbose parser
2024-07-29 20:39 [PATCH v1 0/1] dts: testpmd verbose parser jspewock
` (2 preceding siblings ...)
2024-08-08 20:36 ` [PATCH v3 0/1] dts: testpmd verbose parser jspewock
@ 2024-09-18 16:34 ` jspewock
2024-09-18 16:34 ` [PATCH v4 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-18 17:05 ` [PATCH v5 0/1] dts: testpmd verbose parser jspewock
` (2 subsequent siblings)
6 siblings, 1 reply; 39+ messages in thread
From: jspewock @ 2024-09-18 16:34 UTC (permalink / raw)
To: paul.szczepanek, yoan.picchi, npratte, Honnappa.Nagarahalli,
thomas, probb, Luca.Vizzarro, alex.chapman, wathsala.vithanage,
juraj.linkes
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v4:
* adjust name of OLFlags class to be PacketOffloadFlags
* change values in PacketOffloadFlags to match the ones in
rte_mbuf_core.h
* added documentation from rte_mbuf_core.h to flag values
* added documentation from rte_mbuf_ptype.h to the RtePTypes class
and reference to the file where the definitions are taken from
* fixed typos
* removed find_all() from TextParser
* added support for other types of MAC addresses
* small formatting fixes
Jeremy Spewock (1):
dts: add text parser for testpmd verbose output
dts/framework/remote_session/testpmd_shell.py | 525 +++++++++++++++++-
dts/framework/utils.py | 6 +
2 files changed, 529 insertions(+), 2 deletions(-)
--
2.46.0
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v4 1/1] dts: add text parser for testpmd verbose output
2024-09-18 16:34 ` [PATCH v4 0/1] dts: testpmd verbose parser jspewock
@ 2024-09-18 16:34 ` jspewock
0 siblings, 0 replies; 39+ messages in thread
From: jspewock @ 2024-09-18 16:34 UTC (permalink / raw)
To: paul.szczepanek, yoan.picchi, npratte, Honnappa.Nagarahalli,
thomas, probb, Luca.Vizzarro, alex.chapman, wathsala.vithanage,
juraj.linkes
Cc: dev, Jeremy Spewock
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(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 43e9f56517..2d741802c7 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -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()
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 6b5d5a805f..c040d1cb37 100644
--- 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_HYPEN_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_HYPEN_SEP_MAC}|{_REGEX_FOR_DOT_SEP_MAC}"
+)
def expand_range(range_str: str) -> list[int]:
--
2.46.0
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v5 0/1] dts: testpmd verbose parser
2024-07-29 20:39 [PATCH v1 0/1] dts: testpmd verbose parser jspewock
` (3 preceding siblings ...)
2024-09-18 16:34 ` [PATCH v4 0/1] dts: testpmd verbose parser jspewock
@ 2024-09-18 17:05 ` jspewock
2024-09-18 17:05 ` [PATCH v5 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-25 15:46 ` [PATCH v6 0/1] dts: testpmd verbose parser jspewock
2024-09-26 15:47 ` [PATCH v7 0/1] dts: testpmd verbose parser jspewock
6 siblings, 1 reply; 39+ messages in thread
From: jspewock @ 2024-09-18 17:05 UTC (permalink / raw)
To: alex.chapman, paul.szczepanek, Luca.Vizzarro,
Honnappa.Nagarahalli, wathsala.vithanage, probb, npratte,
juraj.linkes, thomas, yoan.picchi
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v5:
* fix typo
Jeremy Spewock (1):
dts: add text parser for testpmd verbose output
dts/framework/remote_session/testpmd_shell.py | 525 +++++++++++++++++-
dts/framework/utils.py | 6 +
2 files changed, 529 insertions(+), 2 deletions(-)
--
2.46.0
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v5 1/1] dts: add text parser for testpmd verbose output
2024-09-18 17:05 ` [PATCH v5 0/1] dts: testpmd verbose parser jspewock
@ 2024-09-18 17:05 ` jspewock
2024-09-19 9:02 ` Juraj Linkeš
2024-09-19 12:35 ` Juraj Linkeš
0 siblings, 2 replies; 39+ messages in thread
From: jspewock @ 2024-09-18 17:05 UTC (permalink / raw)
To: alex.chapman, paul.szczepanek, Luca.Vizzarro,
Honnappa.Nagarahalli, wathsala.vithanage, probb, npratte,
juraj.linkes, thomas, yoan.picchi
Cc: dev, Jeremy Spewock
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(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 43e9f56517..2d741802c7 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -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()
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 6b5d5a805f..c53cfdf31e 100644
--- 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}"
+_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]:
--
2.46.0
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v5 1/1] dts: add text parser for testpmd verbose output
2024-09-18 17:05 ` [PATCH v5 1/1] dts: add text parser for testpmd verbose output jspewock
@ 2024-09-19 9:02 ` Juraj Linkeš
2024-09-20 15:53 ` Jeremy Spewock
2024-09-19 12:35 ` Juraj Linkeš
1 sibling, 1 reply; 39+ messages in thread
From: Juraj Linkeš @ 2024-09-19 9:02 UTC (permalink / raw)
To: jspewock, alex.chapman, paul.szczepanek, Luca.Vizzarro,
Honnappa.Nagarahalli, wathsala.vithanage, probb, npratte, thomas,
yoan.picchi
Cc: dev
> 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?
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v5 1/1] dts: add text parser for testpmd verbose output
2024-09-18 17:05 ` [PATCH v5 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-19 9:02 ` Juraj Linkeš
@ 2024-09-19 12:35 ` Juraj Linkeš
2024-09-20 15:55 ` Jeremy Spewock
1 sibling, 1 reply; 39+ messages in thread
From: Juraj Linkeš @ 2024-09-19 12:35 UTC (permalink / raw)
To: jspewock, alex.chapman, paul.szczepanek, Luca.Vizzarro,
Honnappa.Nagarahalli, wathsala.vithanage, probb, npratte, thomas,
yoan.picchi
Cc: dev
> 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.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v5 1/1] dts: add text parser for testpmd verbose output
2024-09-19 9:02 ` Juraj Linkeš
@ 2024-09-20 15:53 ` Jeremy Spewock
2024-09-23 13:30 ` Juraj Linkeš
0 siblings, 1 reply; 39+ messages in thread
From: Jeremy Spewock @ 2024-09-20 15:53 UTC (permalink / raw)
To: Juraj Linkeš
Cc: alex.chapman, paul.szczepanek, Luca.Vizzarro,
Honnappa.Nagarahalli, wathsala.vithanage, probb, npratte, thomas,
yoan.picchi, dev
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.
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v5 1/1] dts: add text parser for testpmd verbose output
2024-09-19 12:35 ` Juraj Linkeš
@ 2024-09-20 15:55 ` Jeremy Spewock
0 siblings, 0 replies; 39+ messages in thread
From: Jeremy Spewock @ 2024-09-20 15:55 UTC (permalink / raw)
To: Juraj Linkeš
Cc: alex.chapman, paul.szczepanek, Luca.Vizzarro,
Honnappa.Nagarahalli, wathsala.vithanage, probb, npratte, thomas,
yoan.picchi, dev
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 :).
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v5 1/1] dts: add text parser for testpmd verbose output
2024-09-20 15:53 ` Jeremy Spewock
@ 2024-09-23 13:30 ` Juraj Linkeš
0 siblings, 0 replies; 39+ messages in thread
From: Juraj Linkeš @ 2024-09-23 13:30 UTC (permalink / raw)
To: Jeremy Spewock
Cc: alex.chapman, paul.szczepanek, Luca.Vizzarro,
Honnappa.Nagarahalli, wathsala.vithanage, probb, npratte, thomas,
yoan.picchi, dev
>>
>> 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).
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v6 0/1] dts: testpmd verbose parser
2024-07-29 20:39 [PATCH v1 0/1] dts: testpmd verbose parser jspewock
` (4 preceding siblings ...)
2024-09-18 17:05 ` [PATCH v5 0/1] dts: testpmd verbose parser jspewock
@ 2024-09-25 15:46 ` jspewock
2024-09-25 15:46 ` [PATCH v6 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-26 15:47 ` [PATCH v7 0/1] dts: testpmd verbose parser jspewock
6 siblings, 1 reply; 39+ messages in thread
From: jspewock @ 2024-09-25 15:46 UTC (permalink / raw)
To: Luca.Vizzarro, Honnappa.Nagarahalli, paul.szczepanek,
alex.chapman, probb, juraj.linkes, wathsala.vithanage, thomas,
yoan.picchi, npratte
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v6:
* apply on next-dts
* update PacketOffloadFlag values which were equal to combinations of
other values to make them unique
* remove support of EUI-64 MAC addresses for now
* update RtePtype and PacketOffloadFlag so that their parser methods
don't bother themselves with splitting the string into a list
* reformatted and updated doc-strings
Jeremy Spewock (1):
dts: add text parser for testpmd verbose output
dts/framework/remote_session/testpmd_shell.py | 537 +++++++++++++++++-
dts/framework/utils.py | 3 +
2 files changed, 538 insertions(+), 2 deletions(-)
--
2.46.0
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v6 1/1] dts: add text parser for testpmd verbose output
2024-09-25 15:46 ` [PATCH v6 0/1] dts: testpmd verbose parser jspewock
@ 2024-09-25 15:46 ` jspewock
2024-09-26 8:25 ` Juraj Linkeš
0 siblings, 1 reply; 39+ messages in thread
From: jspewock @ 2024-09-25 15:46 UTC (permalink / raw)
To: Luca.Vizzarro, Honnappa.Nagarahalli, paul.szczepanek,
alex.chapman, probb, juraj.linkes, wathsala.vithanage, thomas,
yoan.picchi, npratte
Cc: dev, Jeremy Spewock
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 | 537 +++++++++++++++++-
dts/framework/utils.py | 3 +
2 files changed, 538 insertions(+), 2 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 77902a468d..4ca34505b7 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -32,7 +32,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
P = ParamSpec("P")
TestPmdShellMethod = Callable[Concatenate["TestPmdShell", P], Any]
@@ -581,6 +581,506 @@ 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.
+
+ 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 = 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. Value is 0 in the DPDK library.
+ 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. 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 = 1 << 24
+
+ #: No information about the RX L4 checksum. Value is 0 in the DPDK library.
+ RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = 1 << 25
+ #: 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. 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 = 1 << 26
+
+ #: 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. Value is 0 in the DPDK library.
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = 1 << 27
+ #: 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. 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 = 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 = 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 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 = 1 << 45
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GRE = 1 << 46
+ #: Value is 3 << 45 in the DPDK library.
+ RTE_MBUF_F_TX_TUNNEL_IPIP = 1 << 61
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GENEVE = 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 = 1 << 62
+ #: Value is 6 << 45 in the DPDK library.
+ RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE = 1 << 63
+ #: Value is 7 << 45 in the DPDK library.
+ RTE_MBUF_F_TX_TUNNEL_GTP = 1 << 64
+ #:
+ RTE_MBUF_F_TX_TUNNEL_ESP = 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 = 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 = 1 << 66
+
+ #: 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 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 = 1 << 67
+ #: 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 = 1 << 53
+ #: UDP cksum of TX pkt. Computed by NIC. Value is 3 << 52 in the DPDK library.
+ RTE_MBUF_F_TX_UDP_CKSUM = 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 = 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(cls, flags: str) -> Self:
+ """Makes an instance from a string containing whitespace-separated the flag members.
+
+ Args:
+ arr: A string containing ol_flag values.
+
+ Returns:
+ A new instance of the flag.
+ """
+ flag = cls(0)
+ for name in flags.split():
+ 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.find(r"ol_flags: ([^\n]+)"),
+ cls.from_str,
+ )
+
+
+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(cls, flags: str) -> Self:
+ """Makes an instance from a string containing whitespace-separated the flag members.
+
+ Args:
+ flags: A string containing the packet types.
+
+ Returns:
+ A new instance of the flag.
+ """
+ flag = cls(0)
+ for name in flags.split():
+ 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.find(f"{'hw' if hw else 'sw'} ptype: ([^-]+)"),
+ cls.from_str,
+ )
+
+
+@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+)"))
+
+
def requires_stopped_ports(func: TestPmdShellMethod) -> TestPmdShellMethod:
"""Decorator for :class:`TestPmdShell` commands methods that require stopped ports.
@@ -714,7 +1214,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:
@@ -725,6 +1225,12 @@ 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 the stop command and all other preceding 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 = self.send_command("stop")
if verify:
@@ -734,6 +1240,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.
@@ -981,6 +1488,32 @@ def set_port_mtu_all(self, mtu: int, verify: bool = True) -> None:
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] = []
+ 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()
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index c768dd0c99..d1eee55c0f 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -29,6 +29,9 @@
from .exception import ConfigurationError, InternalError
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_OR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}[:-]){5}[\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_OR_HYPHEN_SEP_MAC}|{_REGEX_FOR_DOT_SEP_MAC}"
def expand_range(range_str: str) -> list[int]:
--
2.46.0
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v6 1/1] dts: add text parser for testpmd verbose output
2024-09-25 15:46 ` [PATCH v6 1/1] dts: add text parser for testpmd verbose output jspewock
@ 2024-09-26 8:25 ` Juraj Linkeš
2024-09-26 14:43 ` Jeremy Spewock
0 siblings, 1 reply; 39+ messages in thread
From: Juraj Linkeš @ 2024-09-26 8:25 UTC (permalink / raw)
To: jspewock, Luca.Vizzarro, Honnappa.Nagarahalli, paul.szczepanek,
alex.chapman, probb, wathsala.vithanage, thomas, yoan.picchi,
npratte
Cc: dev
> diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
> @@ -581,6 +581,506 @@ class TestPmdPortStats(TextParser):
> tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
>
>
> +class PacketOffloadFlag(Flag):
> + #: 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
There are a few instances with two or three consecutive bits set
expliticly instead with auto(). I don't know if it's better to use
auto() or the explitic value, just wanted to point it out.
> +
> + #: 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
> + @classmethod
> + def from_str(cls, flags: str) -> Self:
Now that we're doing the same thing as the other classes, I think it
makes sense to just flat out copy-paste the from_list_string method.
> + """Makes an instance from a string containing whitespace-separated the flag members.
> +
> + Args:
> + arr: A string containing ol_flag values.
> +
> + Returns:
> + A new instance of the flag.
> + """
> + flag = cls(0)
> + for name in flags.split():
> + if hasattr(cls, name):
This is still different from the other class. I think making these
exactly the same would make it clear what needs to be put into the base
class if we ever create one.
> + flag |= cls[name]
> + return flag
> +
> +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.
I think these are functions (rte_get_ptype_*_name), not methods.
> + """
You didn't update the docstring here (double backticks (for file and
function names) and the References: section).
> + @classmethod
> + def from_str(cls, flags: str) -> Self:
The same comments apply here.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v6 1/1] dts: add text parser for testpmd verbose output
2024-09-26 8:25 ` Juraj Linkeš
@ 2024-09-26 14:43 ` Jeremy Spewock
0 siblings, 0 replies; 39+ messages in thread
From: Jeremy Spewock @ 2024-09-26 14:43 UTC (permalink / raw)
To: Juraj Linkeš
Cc: Luca.Vizzarro, Honnappa.Nagarahalli, paul.szczepanek,
alex.chapman, probb, wathsala.vithanage, thomas, yoan.picchi,
npratte, dev
On Thu, Sep 26, 2024 at 4:25 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
>
> > @@ -581,6 +581,506 @@ class TestPmdPortStats(TextParser):
> > tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
> >
> >
> > +class PacketOffloadFlag(Flag):
>
> > + #: 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
>
> There are a few instances with two or three consecutive bits set
> expliticly instead with auto(). I don't know if it's better to use
> auto() or the explitic value, just wanted to point it out.
>
If it was only two consecutive bits I avoided using auto because I
thought that made it more clear (at least for me, it felt like less
bouncing back and forth). This is a good point though, I also don't
know which is really better.
>
> > +
> > + #: 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
> > + @classmethod
> > + def from_str(cls, flags: str) -> Self:
>
> Now that we're doing the same thing as the other classes, I think it
> makes sense to just flat out copy-paste the from_list_string method.
Sure, that makes sense to me.
>
> > + """Makes an instance from a string containing whitespace-separated the flag members.
> > +
> > + Args:
> > + arr: A string containing ol_flag values.
> > +
> > + Returns:
> > + A new instance of the flag.
> > + """
> > + flag = cls(0)
> > + for name in flags.split():
> > + if hasattr(cls, name):
>
> This is still different from the other class. I think making these
> exactly the same would make it clear what needs to be put into the base
> class if we ever create one.
Ack.
>
> > + flag |= cls[name]
> > + return flag
>
> > +
> > +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.
>
> I think these are functions (rte_get_ptype_*_name), not methods.
>
Ahh, good call. I wasn't thinking of the distinction when I wrote it.
> > + """
>
> You didn't update the docstring here (double backticks (for file and
> function names) and the References: section).
Good catch, I'll fix those as well.
>
>
> > + @classmethod
> > + def from_str(cls, flags: str) -> Self:
>
> The same comments apply here.
Ack.
>
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v7 0/1] dts: testpmd verbose parser
2024-07-29 20:39 [PATCH v1 0/1] dts: testpmd verbose parser jspewock
` (5 preceding siblings ...)
2024-09-25 15:46 ` [PATCH v6 0/1] dts: testpmd verbose parser jspewock
@ 2024-09-26 15:47 ` jspewock
2024-09-26 15:47 ` [PATCH v7 1/1] dts: add text parser for testpmd verbose output jspewock
6 siblings, 1 reply; 39+ messages in thread
From: jspewock @ 2024-09-26 15:47 UTC (permalink / raw)
To: thomas, wathsala.vithanage, paul.szczepanek, Luca.Vizzarro,
probb, npratte, juraj.linkes, Honnappa.Nagarahalli, yoan.picchi,
alex.chapman
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v7:
* update from string methods in both PacketOffloadFlag and RtePType to
match the ones in the rss offload flag
* update the doc-string in RtePType to have proper references to the
DPDK libraries where the information is taken from.
Jeremy Spewock (1):
dts: add text parser for testpmd verbose output
dts/framework/remote_session/testpmd_shell.py | 563 +++++++++++++++++-
dts/framework/utils.py | 3 +
2 files changed, 564 insertions(+), 2 deletions(-)
--
2.46.0
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v7 1/1] dts: add text parser for testpmd verbose output
2024-09-26 15:47 ` [PATCH v7 0/1] dts: testpmd verbose parser jspewock
@ 2024-09-26 15:47 ` jspewock
2024-09-27 9:32 ` Juraj Linkeš
` (2 more replies)
0 siblings, 3 replies; 39+ messages in thread
From: jspewock @ 2024-09-26 15:47 UTC (permalink / raw)
To: thomas, wathsala.vithanage, paul.szczepanek, Luca.Vizzarro,
probb, npratte, juraj.linkes, Honnappa.Nagarahalli, yoan.picchi,
alex.chapman
Cc: dev, Jeremy Spewock
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 | 563 +++++++++++++++++-
dts/framework/utils.py | 3 +
2 files changed, 564 insertions(+), 2 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 77902a468d..55efc9d455 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -32,7 +32,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
P = ParamSpec("P")
TestPmdShellMethod = Callable[Concatenate["TestPmdShell", P], Any]
@@ -581,6 +581,532 @@ 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.
+
+ 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 = 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. Value is 0 in the DPDK library.
+ 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. 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 = 1 << 24
+
+ #: No information about the RX L4 checksum. Value is 0 in the DPDK library.
+ RTE_MBUF_F_RX_L4_CKSUM_UNKNOWN = 1 << 25
+ #: 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. 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 = 1 << 26
+
+ #: 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. Value is 0 in the DPDK library.
+ RTE_MBUF_F_RX_OUTER_L4_CKSUM_UNKNOWN = 1 << 27
+ #: 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. 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 = 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 = 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 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 = 1 << 45
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GRE = 1 << 46
+ #: Value is 3 << 45 in the DPDK library.
+ RTE_MBUF_F_TX_TUNNEL_IPIP = 1 << 61
+ #:
+ RTE_MBUF_F_TX_TUNNEL_GENEVE = 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 = 1 << 62
+ #: Value is 6 << 45 in the DPDK library.
+ RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE = 1 << 63
+ #: Value is 7 << 45 in the DPDK library.
+ RTE_MBUF_F_TX_TUNNEL_GTP = 1 << 64
+ #:
+ RTE_MBUF_F_TX_TUNNEL_ESP = 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 = 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 = 1 << 66
+
+ #: 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 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 = 1 << 67
+ #: 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 = 1 << 53
+ #: UDP cksum of TX pkt. Computed by NIC. Value is 3 << 52 in the DPDK library.
+ RTE_MBUF_F_TX_UDP_CKSUM = 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 = 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_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 = cls(0)
+ for name in names.split():
+ flag |= 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 = 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 = 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_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 = cls(0)
+ for name in names.split():
+ flag |= 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 = 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 = 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+)"))
+
+
def requires_stopped_ports(func: TestPmdShellMethod) -> TestPmdShellMethod:
"""Decorator for :class:`TestPmdShell` commands methods that require stopped ports.
@@ -714,7 +1240,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:
@@ -725,6 +1251,12 @@ 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 the stop command and all other preceding 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 = self.send_command("stop")
if verify:
@@ -734,6 +1266,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.
@@ -981,6 +1514,32 @@ def set_port_mtu_all(self, mtu: int, verify: bool = True) -> None:
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] = []
+ 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()
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index c768dd0c99..d1eee55c0f 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -29,6 +29,9 @@
from .exception import ConfigurationError, InternalError
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_OR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}[:-]){5}[\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_OR_HYPHEN_SEP_MAC}|{_REGEX_FOR_DOT_SEP_MAC}"
def expand_range(range_str: str) -> list[int]:
--
2.46.0
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v7 1/1] dts: add text parser for testpmd verbose output
2024-09-26 15:47 ` [PATCH v7 1/1] dts: add text parser for testpmd verbose output jspewock
@ 2024-09-27 9:32 ` Juraj Linkeš
2024-09-27 11:48 ` Luca Vizzarro
2024-09-30 13:41 ` Juraj Linkeš
2 siblings, 0 replies; 39+ messages in thread
From: Juraj Linkeš @ 2024-09-27 9:32 UTC (permalink / raw)
To: jspewock, thomas, wathsala.vithanage, paul.szczepanek,
Luca.Vizzarro, probb, npratte, Honnappa.Nagarahalli, yoan.picchi,
alex.chapman
Cc: dev
On 26. 9. 2024 17:47, jspewock@iol.unh.edu wrote:
> 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>
> ---
Reviewed-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v7 1/1] dts: add text parser for testpmd verbose output
2024-09-26 15:47 ` [PATCH v7 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-27 9:32 ` Juraj Linkeš
@ 2024-09-27 11:48 ` Luca Vizzarro
2024-09-30 13:41 ` Juraj Linkeš
2 siblings, 0 replies; 39+ messages in thread
From: Luca Vizzarro @ 2024-09-27 11:48 UTC (permalink / raw)
To: jspewock, thomas, wathsala.vithanage, paul.szczepanek, probb,
npratte, juraj.linkes, Honnappa.Nagarahalli, yoan.picchi,
alex.chapman
Cc: dev
Reviewed-by: Luca Vizzarro <luca.vizzarro@arm.com>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v7 1/1] dts: add text parser for testpmd verbose output
2024-09-26 15:47 ` [PATCH v7 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-27 9:32 ` Juraj Linkeš
2024-09-27 11:48 ` Luca Vizzarro
@ 2024-09-30 13:41 ` Juraj Linkeš
2 siblings, 0 replies; 39+ messages in thread
From: Juraj Linkeš @ 2024-09-30 13:41 UTC (permalink / raw)
To: jspewock, thomas, wathsala.vithanage, paul.szczepanek,
Luca.Vizzarro, probb, npratte, Honnappa.Nagarahalli, yoan.picchi,
alex.chapman
Cc: dev
On 26. 9. 2024 17:47, jspewock@iol.unh.edu wrote:
> 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>
Applied to next-dts, thanks.
^ permalink raw reply [flat|nested] 39+ messages in thread
end of thread, other threads:[~2024-09-30 13:41 UTC | newest]
Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-07-29 20:39 [PATCH v1 0/1] dts: testpmd verbose parser jspewock
2024-07-29 20:39 ` [PATCH v1 1/1] dts: add text parser for testpmd verbose output jspewock
2024-07-30 13:34 ` [PATCH v2 0/1] dts: testpmd verbose parser jspewock
2024-07-30 13:34 ` [PATCH v2 1/1] dts: add text parser for testpmd verbose output jspewock
2024-07-30 15:41 ` Nicholas Pratte
2024-07-30 21:30 ` Jeremy Spewock
2024-08-02 14:54 ` Nicholas Pratte
2024-08-02 17:38 ` Jeremy Spewock
2024-08-05 13:20 ` Nicholas Pratte
2024-07-30 21:33 ` Jeremy Spewock
2024-08-01 8:43 ` Luca Vizzarro
2024-08-02 13:40 ` Jeremy Spewock
2024-08-01 8:41 ` Luca Vizzarro
2024-08-02 13:35 ` Jeremy Spewock
2024-08-08 20:36 ` [PATCH v3 0/1] dts: testpmd verbose parser jspewock
2024-08-08 20:36 ` [PATCH v3 1/1] dts: add text parser for testpmd verbose output jspewock
2024-08-08 21:49 ` Jeremy Spewock
2024-08-12 17:32 ` Nicholas Pratte
2024-09-09 11:44 ` Juraj Linkeš
2024-09-17 13:40 ` Jeremy Spewock
2024-09-18 8:09 ` Juraj Linkeš
2024-09-18 16:34 ` [PATCH v4 0/1] dts: testpmd verbose parser jspewock
2024-09-18 16:34 ` [PATCH v4 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-18 17:05 ` [PATCH v5 0/1] dts: testpmd verbose parser jspewock
2024-09-18 17:05 ` [PATCH v5 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-19 9:02 ` Juraj Linkeš
2024-09-20 15:53 ` Jeremy Spewock
2024-09-23 13:30 ` Juraj Linkeš
2024-09-19 12:35 ` Juraj Linkeš
2024-09-20 15:55 ` Jeremy Spewock
2024-09-25 15:46 ` [PATCH v6 0/1] dts: testpmd verbose parser jspewock
2024-09-25 15:46 ` [PATCH v6 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-26 8:25 ` Juraj Linkeš
2024-09-26 14:43 ` Jeremy Spewock
2024-09-26 15:47 ` [PATCH v7 0/1] dts: testpmd verbose parser jspewock
2024-09-26 15:47 ` [PATCH v7 1/1] dts: add text parser for testpmd verbose output jspewock
2024-09-27 9:32 ` Juraj Linkeš
2024-09-27 11:48 ` Luca Vizzarro
2024-09-30 13:41 ` Juraj Linkeš
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).