* [PATCH v1] dts: add VLAN methods to testpmd shell
@ 2024-09-11 17:38 Dean Marx
2024-09-12 2:17 ` Patrick Robb
2024-09-18 19:41 ` [PATCH v2] " Dean Marx
0 siblings, 2 replies; 9+ messages in thread
From: Dean Marx @ 2024-09-11 17:38 UTC (permalink / raw)
To: probb, npratte, jspewock, luca.vizzarro, yoan.picchi,
Honnappa.Nagarahalli, paul.szczepanek, juraj.linkes
Cc: dev, Dean Marx
added the following methods to testpmd shell class:
vlan set filter on/off, rx vlan add/rm,
vlan set strip on/off, port stop/start all/port,
tx vlan set/reset, set promisc/verbose
fixed bug in vlan_offload for
show port info, removed $ at end of regex
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
Reviewed-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 245 +++++++++++++++++-
1 file changed, 244 insertions(+), 1 deletion(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 43e9f56517..66cc5cb289 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -98,7 +98,7 @@ def make_parser(cls) -> ParserFn:
r"strip (?P<STRIP>on|off), "
r"filter (?P<FILTER>on|off), "
r"extend (?P<EXTEND>on|off), "
- r"qinq strip (?P<QINQ_STRIP>on|off)$",
+ r"qinq strip (?P<QINQ_STRIP>on|off)",
re.MULTILINE,
named=True,
),
@@ -806,6 +806,249 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
return TestPmdPortStats.parse(output)
+ def vlan_filter_set(self, port: int, on: bool, verify: bool = True) -> None:
+ """Set vlan filter on.
+
+ Args:
+ port: The port number to enable VLAN filter on, should be within 0-32.
+ on: Sets filter on if :data:`True`, otherwise turns off.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan filtering was enabled successfully.
+ If not, it is considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the filter
+ fails to update.
+ """
+ filter_cmd_output = self.send_command(f"vlan set filter {'on' if on else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if on ^ (vlan_settings is not None and VLANOffloadFlag.FILTER in vlan_settings):
+ self._logger.debug(f"Failed to set filter on port {port}: \n{filter_cmd_output}")
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set VLAN filter on port {port}."
+ )
+
+ def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool = True) -> None:
+ """Add specified vlan tag to the filter list on a port.
+
+ Args:
+ vlan: The vlan tag to add, should be within 1-1005, 1-4094 extended.
+ port: The port number to add the tag on, should be within 0-32.
+ add: Adds the tag if :data:`True`, otherwise removes tag.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ the vlan tag was added to the filter list on the specified port. If not, it is
+ considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the tag
+ is not added.
+ """
+ rx_output = self.send_command(f"rx_vlan {'add' if add else 'rm'} {vlan} {port}")
+ if verify:
+ if (
+ "VLAN-filtering disabled" in rx_output
+ or "Invalid vlan_id" in rx_output
+ or "Bad arguments" in rx_output
+ ):
+ self._logger.debug(
+ f"Failed to {'add' if add else 'remove'} tag {vlan} port {port}: \n{rx_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to {'add' if add else 'remove'} tag {vlan} on port {port}."
+ )
+
+ def vlan_strip_set(self, port: int, on: bool, verify: bool = True) -> None:
+ """Enable vlan stripping on the specified port.
+
+ Args:
+ port: The port number to use, should be within 0-32.
+ on: If :data:`True`, will turn strip on, otherwise will turn off.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan stripping was enabled on the specified port.
+ If not, it is considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and stripping
+ fails to update.
+ """
+ strip_output = self.send_command(f"vlan set strip {'on' if on else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if on ^ (vlan_settings is not None and VLANOffloadFlag.STRIP in vlan_settings):
+ self._logger.debug(
+ f"Failed to set strip {'on' if on else 'off'} port {port}: \n{strip_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set strip {'on' if on else 'off'} port {port}."
+ )
+
+ def port_stop_all(self, verify: bool = True) -> None:
+ """Stop all ports.
+
+ Args:
+ verify: If :data:`True`, the output of the command is scanned
+ to ensure all ports are stopped. If not, it is considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and all ports
+ fail to stop.
+ """
+ port_output = self.send_command("port stop all")
+ if verify:
+ if "Done" not in port_output:
+ self._logger.debug(f"Failed to stop all ports: \n{port_output}")
+ raise InteractiveCommandExecutionError("Testpmd failed to stop all ports.")
+
+ def port_stop(self, port: int, verify: bool = True) -> None:
+ """Stop specified port.
+
+ Args:
+ port: Specifies the port number to use, must be between 0-32.
+ verify: If :data:`True`, the output of the command is scanned
+ to ensure specified port is stopped. If not, it is considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the port
+ is not stopped.
+ """
+ port_output = self.send_command(f"port stop {port}")
+ if verify:
+ if "Done" not in port_output:
+ self._logger.debug(f"Failed to stop port {port}: \n{port_output}")
+ raise InteractiveCommandExecutionError(f"Testpmd failed to stop port {port}.")
+
+ def port_start_all(self, verify: bool = True) -> None:
+ """Start all ports.
+
+ Args:
+ verify: If :data:`True`, the output of the command is scanned
+ to ensure all ports are started. If not, it is considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and all ports
+ fail to start.
+ """
+ port_output = self.send_command("port start all")
+ if verify:
+ if "Done" not in port_output:
+ self._logger.debug(f"Failed to start all ports: \n{port_output}")
+ raise InteractiveCommandExecutionError("Testpmd failed to start all ports.")
+
+ def port_start(self, port: int, verify: bool = True) -> None:
+ """Start specified port.
+
+ Args:
+ port: Specifies the port number to use, must be between 0-32.
+ verify: If :data:`True`, the output of the command is scanned
+ to ensure specified port is started. If not, it is considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the port
+ is not started.
+ """
+ port_output = self.send_command(f"port start {port}")
+ if verify:
+ if "Done" not in port_output:
+ self._logger.debug(f"Failed to start port {port}: \n{port_output}")
+ raise InteractiveCommandExecutionError(f"Testpmd failed to start port {port}.")
+
+ def tx_vlan_set(self, port: int, vlan: int, verify: bool = True) -> None:
+ """Set hardware insertion of vlan tags in packets sent on a port.
+
+ Args:
+ port: The port number to use, should be within 0-32.
+ vlan: The vlan tag to insert, should be within 1-4094.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ vlan insertion was enabled on the specified port. If not, it is
+ considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
+ tag is not set.
+ """
+ vlan_insert_output = self.send_command(f"tx_vlan set {port} {vlan}")
+ if verify:
+ if (
+ "Please stop port" in vlan_insert_output
+ or "Invalid vlan_id" in vlan_insert_output
+ or "Invalid port" in vlan_insert_output
+ ):
+ self._logger.debug(
+ f"Failed to set vlan tag {vlan} on port {port}:\n{vlan_insert_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set vlan insertion tag {vlan} on port {port}."
+ )
+
+ def tx_vlan_reset(self, port: int, verify: bool = True) -> None:
+ """Disable hardware insertion of vlan tags in packets sent on a port.
+
+ Args:
+ port: The port number to use, should be within 0-32.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ vlan insertion was disabled on the specified port. If not, it is
+ considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
+ tag is not reset.
+ """
+ vlan_insert_output = self.send_command(f"tx_vlan reset {port}")
+ if verify:
+ if "Please stop port" in vlan_insert_output or "Invalid port" in vlan_insert_output:
+ self._logger.debug(
+ f"Failed to reset vlan insertion on port {port}: \n{vlan_insert_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to reset vlan insertion on port {port}."
+ )
+
+ def set_promisc(self, port: int, on: bool, verify: bool = True) -> None:
+ """Turns promiscuous mode on/off for the specified port.
+
+ Args:
+ port: Port number to use, should be within 0-32.
+ on: If :data:`True`, turn promisc mode on, otherwise turn off.
+ verify: If :data:`True` an additional command will be sent to verify that promisc mode
+ is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and promisc mode
+ is not correctly set.
+ """
+ promisc_output = self.send_command(f"set promisc {port} {'on' if on else 'off'}")
+ if verify:
+ stats = self.show_port_info(port_id=port)
+ if on ^ stats.is_promiscuous_mode_enabled:
+ self._logger.debug(f"Failed to set promisc mode on port {port}: \n{promisc_output}")
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set promisc mode on port {port}."
+ )
+
+ def set_verbose(self, level: int, verify: bool = True) -> None:
+ """Set debug verbosity level.
+
+ Args:
+ level: 0 - silent except for error
+ 1 - fully verbose except for Tx packets
+ 2 - fully verbose except for Rx packets
+ >2 - fully verbose
+ verify: If :data:`True` the command output will be scanned to verify that verbose level
+ is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and verbose level
+ is not correctly set.
+ """
+ verbose_output = self.send_command(f"set verbose {level}")
+ if verify:
+ if "Change verbose level" not in verbose_output:
+ self._logger.debug(f"Failed to set verbose level to {level}: \n{verbose_output}")
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set verbose level to {level}."
+ )
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
--
2.44.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1] dts: add VLAN methods to testpmd shell
2024-09-11 17:38 [PATCH v1] dts: add VLAN methods to testpmd shell Dean Marx
@ 2024-09-12 2:17 ` Patrick Robb
2024-09-18 19:41 ` [PATCH v2] " Dean Marx
1 sibling, 0 replies; 9+ messages in thread
From: Patrick Robb @ 2024-09-12 2:17 UTC (permalink / raw)
To: Dean Marx
Cc: npratte, jspewock, luca.vizzarro, yoan.picchi,
Honnappa.Nagarahalli, paul.szczepanek, juraj.linkes, dev
A new version should be submitted which will apply onto next-dts.
Mostly looks good barring 1 or 2 nits and some overlap with Luca's
pktgen/testpmd series which was merged to next-dts the other day.
On Wed, Sep 11, 2024 at 1:37 PM Dean Marx <dmarx@iol.unh.edu> wrote:
>
> added the following methods to testpmd shell class:
> vlan set filter on/off, rx vlan add/rm,
> vlan set strip on/off, port stop/start all/port,
> tx vlan set/reset, set promisc/verbose
>
> fixed bug in vlan_offload for
> show port info, removed $ at end of regex
I think to align with the submission guidelines, commit body should include:
Fixes: 61d5bc9bf974 ("dts: add port info command to testpmd shell")
> + def vlan_filter_set(self, port: int, on: bool, verify: bool = True) -> None:
> + """Set vlan filter on.
> +
> + Args:
> + port: The port number to enable VLAN filter on, should be within 0-32.
> + on: Sets filter on if :data:`True`, otherwise turns off.
> + verify: If :data:`True`, the output of the command and show port info
> + is scanned to verify that vlan filtering was enabled successfully.
> + If not, it is considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the filter
> + fails to update.
> + """
> + filter_cmd_output = self.send_command(f"vlan set filter {'on' if on else 'off'} {port}")
> + if verify:
> + vlan_settings = self.show_port_info(port_id=port).vlan_offload
> + if on ^ (vlan_settings is not None and VLANOffloadFlag.FILTER in vlan_settings):
Thank you for teaching me this syntax for python xor :)
> + self._logger.debug(f"Failed to set filter on port {port}: \n{filter_cmd_output}")
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to set VLAN filter on port {port}."
> + )
> +
> + def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool = True) -> None:
> + """Add specified vlan tag to the filter list on a port.
> +
> + Args:
> + vlan: The vlan tag to add, should be within 1-1005, 1-4094 extended.
> + port: The port number to add the tag on, should be within 0-32.
> + add: Adds the tag if :data:`True`, otherwise removes tag.
> + verify: If :data:`True`, the output of the command is scanned to verify that
> + the vlan tag was added to the filter list on the specified port. If not, it is
> + considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the tag
> + is not added.
> + """
> + rx_output = self.send_command(f"rx_vlan {'add' if add else 'rm'} {vlan} {port}")
I guess this variable should be named rx_cmd_output, or
rx_vlan_cmd_output to remain consistent with the preceding method, but
not a huge deal.
> + if verify:
> + if (
> + "VLAN-filtering disabled" in rx_output
> + or "Invalid vlan_id" in rx_output
> + or "Bad arguments" in rx_output
> + ):
> + self._logger.debug(
> + f"Failed to {'add' if add else 'remove'} tag {vlan} port {port}: \n{rx_output}"
> + )
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to {'add' if add else 'remove'} tag {vlan} on port {port}."
> + )
> +
> + def vlan_strip_set(self, port: int, on: bool, verify: bool = True) -> None:
> + """Enable vlan stripping on the specified port.
> +
> + Args:
> + port: The port number to use, should be within 0-32.
> + on: If :data:`True`, will turn strip on, otherwise will turn off.
> + verify: If :data:`True`, the output of the command and show port info
> + is scanned to verify that vlan stripping was enabled on the specified port.
> + If not, it is considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and stripping
> + fails to update.
> + """
> + strip_output = self.send_command(f"vlan set strip {'on' if on else 'off'} {port}")
> + if verify:
> + vlan_settings = self.show_port_info(port_id=port).vlan_offload
> + if on ^ (vlan_settings is not None and VLANOffloadFlag.STRIP in vlan_settings):
> + self._logger.debug(
> + f"Failed to set strip {'on' if on else 'off'} port {port}: \n{strip_output}"
> + )
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to set strip {'on' if on else 'off'} port {port}."
> + )
> +
> + def port_stop_all(self, verify: bool = True) -> None:
> + """Stop all ports.
Luca's pktgen and testpmd change series included an equivalent method
for stopping all ports, so this can be removed.
> +
> + Args:
> + verify: If :data:`True`, the output of the command is scanned
> + to ensure all ports are stopped. If not, it is considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and all ports
> + fail to stop.
> + """
> + port_output = self.send_command("port stop all")
> + if verify:
> + if "Done" not in port_output:
> + self._logger.debug(f"Failed to stop all ports: \n{port_output}")
> + raise InteractiveCommandExecutionError("Testpmd failed to stop all ports.")
> +
> + def port_stop(self, port: int, verify: bool = True) -> None:
> + """Stop specified port.
There is a new TestPmdShell attribute, ports_started, which may need
to be updated when this is run. You may have to iterate through
self._ports and scan testpmd for port info or check otherwise and
modify ports_started depending on the result.
> +
> + Args:
> + port: Specifies the port number to use, must be between 0-32.
> + verify: If :data:`True`, the output of the command is scanned
> + to ensure specified port is stopped. If not, it is considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the port
> + is not stopped.
> + """
> + port_output = self.send_command(f"port stop {port}")
> + if verify:
> + if "Done" not in port_output:
> + self._logger.debug(f"Failed to stop port {port}: \n{port_output}")
> + raise InteractiveCommandExecutionError(f"Testpmd failed to stop port {port}.")
> +
> + def port_start_all(self, verify: bool = True) -> None:
> + """Start all ports.
> +
> + Args:
> + verify: If :data:`True`, the output of the command is scanned
> + to ensure all ports are started. If not, it is considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and all ports
> + fail to start.
> + """
> + port_output = self.send_command("port start all")
> + if verify:
> + if "Done" not in port_output:
> + self._logger.debug(f"Failed to start all ports: \n{port_output}")
> + raise InteractiveCommandExecutionError("Testpmd failed to start all ports.")
> +
> + def port_start(self, port: int, verify: bool = True) -> None:
> + """Start specified port.
Same possible concern as described for port_start()
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] dts: add VLAN methods to testpmd shell
2024-09-11 17:38 [PATCH v1] dts: add VLAN methods to testpmd shell Dean Marx
2024-09-12 2:17 ` Patrick Robb
@ 2024-09-18 19:41 ` Dean Marx
2024-09-20 18:33 ` Jeremy Spewock
` (2 more replies)
1 sibling, 3 replies; 9+ messages in thread
From: Dean Marx @ 2024-09-18 19:41 UTC (permalink / raw)
To: probb, npratte, jspewock, luca.vizzarro, yoan.picchi,
Honnappa.Nagarahalli, paul.szczepanek, juraj.linkes
Cc: dev, Dean Marx
added the following methods to testpmd shell class:
vlan set filter on/off, rx vlan add/rm,
vlan set strip on/off, tx vlan set/reset,
set promisc/verbose
Fixes: 61d5bc9bf974 ("dts: add port info command to testpmd shell")
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 175 +++++++++++++++++-
1 file changed, 174 insertions(+), 1 deletion(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 8c228af39f..5c5e681841 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -102,7 +102,7 @@ def make_parser(cls) -> ParserFn:
r"strip (?P<STRIP>on|off), "
r"filter (?P<FILTER>on|off), "
r"extend (?P<EXTEND>on|off), "
- r"qinq strip (?P<QINQ_STRIP>on|off)$",
+ r"qinq strip (?P<QINQ_STRIP>on|off)",
re.MULTILINE,
named=True,
),
@@ -982,6 +982,179 @@ 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)
+ def vlan_filter_set(self, port: int, on: bool, verify: bool = True) -> None:
+ """Set vlan filter on.
+
+ Args:
+ port: The port number to enable VLAN filter on, should be within 0-32.
+ on: Sets filter on if :data:`True`, otherwise turns off.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan filtering was enabled successfully.
+ If not, it is considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the filter
+ fails to update.
+ """
+ filter_cmd_output = self.send_command(f"vlan set filter {'on' if on else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if on ^ (vlan_settings is not None and VLANOffloadFlag.FILTER in vlan_settings):
+ self._logger.debug(f"Failed to set filter on port {port}: \n{filter_cmd_output}")
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set VLAN filter on port {port}."
+ )
+
+ def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool = True) -> None:
+ """Add specified vlan tag to the filter list on a port.
+
+ Args:
+ vlan: The vlan tag to add, should be within 1-1005, 1-4094 extended.
+ port: The port number to add the tag on, should be within 0-32.
+ add: Adds the tag if :data:`True`, otherwise removes tag.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ the vlan tag was added to the filter list on the specified port. If not, it is
+ considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the tag
+ is not added.
+ """
+ rx_output = self.send_command(f"rx_vlan {'add' if add else 'rm'} {vlan} {port}")
+ if verify:
+ if (
+ "VLAN-filtering disabled" in rx_output
+ or "Invalid vlan_id" in rx_output
+ or "Bad arguments" in rx_output
+ ):
+ self._logger.debug(
+ f"Failed to {'add' if add else 'remove'} tag {vlan} port {port}: \n{rx_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to {'add' if add else 'remove'} tag {vlan} on port {port}."
+ )
+
+ def vlan_strip_set(self, port: int, on: bool, verify: bool = True) -> None:
+ """Enable vlan stripping on the specified port.
+
+ Args:
+ port: The port number to use, should be within 0-32.
+ on: If :data:`True`, will turn strip on, otherwise will turn off.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan stripping was enabled on the specified port.
+ If not, it is considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and stripping
+ fails to update.
+ """
+ strip_output = self.send_command(f"vlan set strip {'on' if on else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if on ^ (vlan_settings is not None and VLANOffloadFlag.STRIP in vlan_settings):
+ self._logger.debug(
+ f"Failed to set strip {'on' if on else 'off'} port {port}: \n{strip_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set strip {'on' if on else 'off'} port {port}."
+ )
+
+ def tx_vlan_set(self, port: int, vlan: int, verify: bool = True) -> None:
+ """Set hardware insertion of vlan tags in packets sent on a port.
+
+ Args:
+ port: The port number to use, should be within 0-32.
+ vlan: The vlan tag to insert, should be within 1-4094.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ vlan insertion was enabled on the specified port. If not, it is
+ considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
+ tag is not set.
+ """
+ vlan_insert_output = self.send_command(f"tx_vlan set {port} {vlan}")
+ if verify:
+ if (
+ "Please stop port" in vlan_insert_output
+ or "Invalid vlan_id" in vlan_insert_output
+ or "Invalid port" in vlan_insert_output
+ ):
+ self._logger.debug(
+ f"Failed to set vlan tag {vlan} on port {port}:\n{vlan_insert_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set vlan insertion tag {vlan} on port {port}."
+ )
+
+ def tx_vlan_reset(self, port: int, verify: bool = True) -> None:
+ """Disable hardware insertion of vlan tags in packets sent on a port.
+
+ Args:
+ port: The port number to use, should be within 0-32.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ vlan insertion was disabled on the specified port. If not, it is
+ considered an error.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
+ tag is not reset.
+ """
+ vlan_insert_output = self.send_command(f"tx_vlan reset {port}")
+ if verify:
+ if "Please stop port" in vlan_insert_output or "Invalid port" in vlan_insert_output:
+ self._logger.debug(
+ f"Failed to reset vlan insertion on port {port}: \n{vlan_insert_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to reset vlan insertion on port {port}."
+ )
+
+ def set_promisc(self, port: int, on: bool, verify: bool = True) -> None:
+ """Turns promiscuous mode on/off for the specified port.
+
+ Args:
+ port: Port number to use, should be within 0-32.
+ on: If :data:`True`, turn promisc mode on, otherwise turn off.
+ verify: If :data:`True` an additional command will be sent to verify that promisc mode
+ is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and promisc mode
+ is not correctly set.
+ """
+ promisc_output = self.send_command(f"set promisc {port} {'on' if on else 'off'}")
+ if verify:
+ stats = self.show_port_info(port_id=port)
+ if on ^ stats.is_promiscuous_mode_enabled:
+ self._logger.debug(f"Failed to set promisc mode on port {port}: \n{promisc_output}")
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set promisc mode on port {port}."
+ )
+
+ def set_verbose(self, level: int, verify: bool = True) -> None:
+ """Set debug verbosity level.
+
+ Args:
+ level: 0 - silent except for error
+ 1 - fully verbose except for Tx packets
+ 2 - fully verbose except for Rx packets
+ >2 - fully verbose
+ verify: If :data:`True` the command output will be scanned to verify that verbose level
+ is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and verbose level
+ is not correctly set.
+ """
+ verbose_output = self.send_command(f"set verbose {level}")
+ if verify:
+ if "Change verbose level" not in verbose_output:
+ self._logger.debug(f"Failed to set verbose level to {level}: \n{verbose_output}")
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set verbose level to {level}."
+ )
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
--
2.44.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] dts: add VLAN methods to testpmd shell
2024-09-18 19:41 ` [PATCH v2] " Dean Marx
@ 2024-09-20 18:33 ` Jeremy Spewock
2024-09-24 13:56 ` Juraj Linkeš
2024-10-08 17:15 ` Dean Marx
2 siblings, 0 replies; 9+ messages in thread
From: Jeremy Spewock @ 2024-09-20 18:33 UTC (permalink / raw)
To: Dean Marx
Cc: probb, npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek, juraj.linkes, dev
On Wed, Sep 18, 2024 at 3:41 PM Dean Marx <dmarx@iol.unh.edu> wrote:
>
<snip>
> +
> + def tx_vlan_set(self, port: int, vlan: int, verify: bool = True) -> None:
One thing to note is that I think this method (unlike rx_vlan_set for
some reason) requires the ports to be stopped for it to work, so we
should probably decorate this with @requires_stopped_ports to make it
more convenient for the developer.
> + """Set hardware insertion of vlan tags in packets sent on a port.
> +
> + Args:
> + port: The port number to use, should be within 0-32.
> + vlan: The vlan tag to insert, should be within 1-4094.
> + verify: If :data:`True`, the output of the command is scanned to verify that
> + vlan insertion was enabled on the specified port. If not, it is
> + considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
> + tag is not set.
> + """
> + vlan_insert_output = self.send_command(f"tx_vlan set {port} {vlan}")
> + if verify:
> + if (
> + "Please stop port" in vlan_insert_output
> + or "Invalid vlan_id" in vlan_insert_output
> + or "Invalid port" in vlan_insert_output
> + ):
> + self._logger.debug(
> + f"Failed to set vlan tag {vlan} on port {port}:\n{vlan_insert_output}"
> + )
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to set vlan insertion tag {vlan} on port {port}."
> + )
> +
> + def tx_vlan_reset(self, port: int, verify: bool = True) -> None:
I believe this method also required ports to be stopped.
> + """Disable hardware insertion of vlan tags in packets sent on a port.
> +
> + Args:
> + port: The port number to use, should be within 0-32.
> + verify: If :data:`True`, the output of the command is scanned to verify that
> + vlan insertion was disabled on the specified port. If not, it is
> + considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
> + tag is not reset.
> + """
> + vlan_insert_output = self.send_command(f"tx_vlan reset {port}")
> + if verify:
> + if "Please stop port" in vlan_insert_output or "Invalid port" in vlan_insert_output:
> + self._logger.debug(
> + f"Failed to reset vlan insertion on port {port}: \n{vlan_insert_output}"
> + )
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to reset vlan insertion on port {port}."
> + )
> +
<snip>
> 2.44.0
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] dts: add VLAN methods to testpmd shell
2024-09-18 19:41 ` [PATCH v2] " Dean Marx
2024-09-20 18:33 ` Jeremy Spewock
@ 2024-09-24 13:56 ` Juraj Linkeš
2024-10-08 17:15 ` Dean Marx
2 siblings, 0 replies; 9+ messages in thread
From: Juraj Linkeš @ 2024-09-24 13:56 UTC (permalink / raw)
To: Dean Marx, probb, npratte, jspewock, luca.vizzarro, yoan.picchi,
Honnappa.Nagarahalli, paul.szczepanek
Cc: dev
We should bring up the exact names of method used in this patch and talk
through them in the call.
On 18. 9. 2024 21:41, Dean Marx wrote:
> added the following methods to testpmd shell class:
Capitalize please.
> vlan set filter on/off, rx vlan add/rm,
> vlan set strip on/off, tx vlan set/reset,
> set promisc/verbose
>
> Fixes: 61d5bc9bf974 ("dts: add port info command to testpmd shell")
Is the fix related to the rest of the changes? It looks like it is. The
relation to the other changes should be explained in the commit message
as well as what the fix fixes (or in which cases the original
implementation didn't work properly).
>
> Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
> ---
> dts/framework/remote_session/testpmd_shell.py | 175 +++++++++++++++++-
> 1 file changed, 174 insertions(+), 1 deletion(-)
>
> diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
> index 8c228af39f..5c5e681841 100644
> --- a/dts/framework/remote_session/testpmd_shell.py
> +++ b/dts/framework/remote_session/testpmd_shell.py
> @@ -102,7 +102,7 @@ def make_parser(cls) -> ParserFn:
> r"strip (?P<STRIP>on|off), "
> r"filter (?P<FILTER>on|off), "
> r"extend (?P<EXTEND>on|off), "
> - r"qinq strip (?P<QINQ_STRIP>on|off)$",
> + r"qinq strip (?P<QINQ_STRIP>on|off)",
> re.MULTILINE,
> named=True,
> ),
> @@ -982,6 +982,179 @@ 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)
>
> + def vlan_filter_set(self, port: int, on: bool, verify: bool = True) -> None:
The names should follow the same naming scheme. I think the scheme says
it doesn't necessarily have to follow testpmd commands, so I'd say let's
name it the same way as the mtu methods - set_vlan_filter.
> + """Set vlan filter on.
Out of curiosity, what is vlan filter?
> +
> + Args:
> + port: The port number to enable VLAN filter on, should be within 0-32.
Where is this 0-32 coming from? And why 32 and not 31?
> + on: Sets filter on if :data:`True`, otherwise turns off.
I'd use third person and add the port:
Enable the filter on `port` if :data:`True`, otherwise disable it.
> + verify: If :data:`True`, the output of the command and show port info
> + is scanned to verify that vlan filtering was enabled successfully.
> + If not, it is considered an error.
This is not 100% clear whether this means if False or if not enabled
successfully. Looks like this should be updated everywhere.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the filter
> + fails to update.
> + """
> + filter_cmd_output = self.send_command(f"vlan set filter {'on' if on else 'off'} {port}")
> + if verify:
> + vlan_settings = self.show_port_info(port_id=port).vlan_offload
> + if on ^ (vlan_settings is not None and VLANOffloadFlag.FILTER in vlan_settings):
This is an awesome condition.
> + self._logger.debug(f"Failed to set filter on port {port}: \n{filter_cmd_output}")
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to set VLAN filter on port {port}."
> + )
We should say whether we're enabling or disabling the filter in both the
log and error. Does filter_cmd_output contain this? What about the other
commands (every one of them except verbose_output)?
> +
> + def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool = True) -> None:
We have set as the prefix for boolean configs and configs that only set
the value, here we probabaly want add_del_rx_vlan. We really should talk
about these names in the call.
> + """Add specified vlan tag to the filter list on a port.
The command doesn't mention the filter. Does this mean the filter needs
to be enabled before we can config vlans on the port?
> +
> + Args:
> + vlan: The vlan tag to add, should be within 1-1005, 1-4094 extended.
The method mentions extended vlans only in this place. It's not clear
when can we use extended vlans (where is it configured or enforced).
> + port: The port number to add the tag on, should be within 0-32.
> + add: Adds the tag if :data:`True`, otherwise removes tag.
removes the tag
> + verify: If :data:`True`, the output of the command is scanned to verify that
> + the vlan tag was added to the filter list on the specified port. If not, it is
> + considered an error.> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the tag
> + is not added.
I guess this would also be raised if removal is unsuccessful.
> + """
> + rx_output = self.send_command(f"rx_vlan {'add' if add else 'rm'} {vlan} {port}")
As Patrick mentioned, the other method has cmd in the name of the
variable, it would be useful here as well.
> + if verify:
> + if (
> + "VLAN-filtering disabled" in rx_output
> + or "Invalid vlan_id" in rx_output
> + or "Bad arguments" in rx_output
> + ):
> + self._logger.debug(
> + f"Failed to {'add' if add else 'remove'} tag {vlan} port {port}: \n{rx_output}"
> + )
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to {'add' if add else 'remove'} tag {vlan} on port {port}."
> + )
> +
> + def vlan_strip_set(self, port: int, on: bool, verify: bool = True) -> None:
> + """Enable vlan stripping on the specified port.
This should say enable or disable.
> +
> + Args:
> + port: The port number to use, should be within 0-32.
> + on: If :data:`True`, will turn strip on, otherwise will turn off.
Let's not be lazy: turn vlan stripping on
> + verify: If :data:`True`, the output of the command and show port info
> + is scanned to verify that vlan stripping was enabled on the specified port.
> + If not, it is considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and stripping
> + fails to update.
> + """
> + strip_output = self.send_command(f"vlan set strip {'on' if on else 'off'} {port}")
Also add cmd to the var name.
> + if verify:
> + vlan_settings = self.show_port_info(port_id=port).vlan_offload
> + if on ^ (vlan_settings is not None and VLANOffloadFlag.STRIP in vlan_settings):
> + self._logger.debug(
> + f"Failed to set strip {'on' if on else 'off'} port {port}: \n{strip_output}"
> + )
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to set strip {'on' if on else 'off'} port {port}."
> + )
> +
> + def tx_vlan_set(self, port: int, vlan: int, verify: bool = True) -> None:
> + """Set hardware insertion of vlan tags in packets sent on a port.
> +
> + Args:
> + port: The port number to use, should be within 0-32.
> + vlan: The vlan tag to insert, should be within 1-4094.
This says 1-4094 right away. Both docstrings should say the same thing.
> + verify: If :data:`True`, the output of the command is scanned to verify that
> + vlan insertion was enabled on the specified port. If not, it is
> + considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
> + tag is not set.
> + """
> + vlan_insert_output = self.send_command(f"tx_vlan set {port} {vlan}")
Also add cmd to the var name.
> + if verify:
> + if (
> + "Please stop port" in vlan_insert_output
> + or "Invalid vlan_id" in vlan_insert_output
> + or "Invalid port" in vlan_insert_output
> + ):
> + self._logger.debug(
> + f"Failed to set vlan tag {vlan} on port {port}:\n{vlan_insert_output}"
> + )
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to set vlan insertion tag {vlan} on port {port}."
> + )
> +
> + def tx_vlan_reset(self, port: int, verify: bool = True) -> None:
I think this could be merged with the previous method. We only need to
add the "on" parameter. Now that I think about it, we should rename on
-> enable, as we'll have a clear, unified name regardless of what the
actual command uses (on, off, set, reset, etc.).
> + """Disable hardware insertion of vlan tags in packets sent on a port.
> +
> + Args:
> + port: The port number to use, should be within 0-32.
> + verify: If :data:`True`, the output of the command is scanned to verify that
> + vlan insertion was disabled on the specified port. If not, it is
> + considered an error.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
> + tag is not reset.
> + """
> + vlan_insert_output = self.send_command(f"tx_vlan reset {port}")
> + if verify:
> + if "Please stop port" in vlan_insert_output or "Invalid port" in vlan_insert_output:
> + self._logger.debug(
> + f"Failed to reset vlan insertion on port {port}: \n{vlan_insert_output}"
> + )
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to reset vlan insertion on port {port}."
> + )
> +
> + def set_promisc(self, port: int, on: bool, verify: bool = True) -> None:
> + """Turns promiscuous mode on/off for the specified port.
In line with previous comments, this should say Enable/disable
promiscuous mode on the specified port.
> +
> + Args:
> + port: Port number to use, should be within 0-32.
> + on: If :data:`True`, turn promisc mode on, otherwise turn off.
> + verify: If :data:`True` an additional command will be sent to verify that promisc mode
> + is properly set. Defaults to :data:`True`.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and promisc mode
> + is not correctly set.
> + """
promisc -> promiscuous in the whole docstring
> + promisc_output = self.send_command(f"set promisc {port} {'on' if on else 'off'}")
> + if verify:
> + stats = self.show_port_info(port_id=port)
> + if on ^ stats.is_promiscuous_mode_enabled:
> + self._logger.debug(f"Failed to set promisc mode on port {port}: \n{promisc_output}")
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to set promisc mode on port {port}."
> + )
And in these two: promisc -> promiscuous.
> +
> + def set_verbose(self, level: int, verify: bool = True) -> None:
> + """Set debug verbosity level.
> +
> + Args:
> + level: 0 - silent except for error
> + 1 - fully verbose except for Tx packets
> + 2 - fully verbose except for Rx packets
> + >2 - fully verbose
> + verify: If :data:`True` the command output will be scanned to verify that verbose level
> + is properly set. Defaults to :data:`True`.
> +
> + Raises:
> + InteractiveCommandExecutionError: If `verify` is :data:`True` and verbose level
> + is not correctly set.
> + """
> + verbose_output = self.send_command(f"set verbose {level}")
Also add cmd to the var name.
> + if verify:
> + if "Change verbose level" not in verbose_output:
> + self._logger.debug(f"Failed to set verbose level to {level}: \n{verbose_output}")
> + raise InteractiveCommandExecutionError(
> + f"Testpmd failed to set verbose level to {level}."
> + )
> +
> def _close(self) -> None:
> """Overrides :meth:`~.interactive_shell.close`."""
> self.stop()
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] dts: add VLAN methods to testpmd shell
2024-09-18 19:41 ` [PATCH v2] " Dean Marx
2024-09-20 18:33 ` Jeremy Spewock
2024-09-24 13:56 ` Juraj Linkeš
@ 2024-10-08 17:15 ` Dean Marx
2024-10-08 18:54 ` [PATCH v3] " Dean Marx
2024-10-08 19:07 ` Dean Marx
2 siblings, 2 replies; 9+ messages in thread
From: Dean Marx @ 2024-10-08 17:15 UTC (permalink / raw)
To: probb, npratte, jspewock, luca.vizzarro, yoan.picchi,
Honnappa.Nagarahalli, paul.szczepanek, juraj.linkes
Cc: dev, Dean Marx
Added the following methods to testpmd shell class:
set vlan filter on/off, rx vlan add/rm,
set vlan strip on/off, tx vlan set/reset,
set promisc/verbose. Fixed a bug in the VLAN regex
used in testpmd shell flags.
Fixes: 61d5bc9bf974 ("dts: add port info command to testpmd shell")
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 192 ++++++++++++++++--
1 file changed, 172 insertions(+), 20 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 88eceb1c87..575261585f 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -102,7 +102,7 @@ def make_parser(cls) -> ParserFn:
r"strip (?P<STRIP>on|off), "
r"filter (?P<FILTER>on|off), "
r"extend (?P<EXTEND>on|off), "
- r"qinq strip (?P<QINQ_STRIP>on|off)$",
+ r"qinq strip (?P<QINQ_STRIP>on|off)",
re.MULTILINE,
named=True,
),
@@ -670,25 +670,6 @@ def __init__(
TestPmdParams(**app_params),
name,
)
-<<<<<<< HEAD
- self._ports = None
-
- @property
- def ports(self) -> list[TestPmdPort]:
- """The ports of the instance.
-
- This caches the ports returned by :meth:`show_port_info_all`.
- To force an update of port information, execute :meth:`show_port_info_all` or
- :meth:`show_port_info`.
-
- Returns: The list of known testpmd ports.
- """
- if self._ports is None:
- return self.show_port_info_all()
- return self._ports
-
-=======
->>>>>>> 8538685a3a86a021c2a634b8f948ae0a39331a26
self.ports_started = not self._app_params.disable_device_start
self._ports = None
@@ -1000,6 +981,177 @@ 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)
+ def set_vlan_filter(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Set vlan filter on.
+
+ Args:
+ port: The port number to enable VLAN filter on.
+ enable: Enable the filter on `port` if :data:`True`, otherwise disable it.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan filtering was set successfully.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the filter
+ fails to update.
+ """
+ filter_cmd_output = self.send_command(f"vlan set filter {'on' if enable else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if enable ^ (vlan_settings is not None and VLANOffloadFlag.FILTER in vlan_settings):
+ self._logger.debug(
+ f"""Failed to {'enable' if enable else 'disable'}
+ filter on port {port}: \n{filter_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"""Failed to {'enable' if enable else 'disable'}
+ filter on port {port}"""
+ )
+
+ def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool = True) -> None:
+ """Add specified vlan tag to the filter list on a port. Requires vlan filter to be on.
+
+ Args:
+ vlan: The vlan tag to add, should be within 1-1005.
+ port: The port number to add the tag on.
+ add: Adds the tag if :data:`True`, otherwise removes the tag.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ the vlan tag was added to the filter list on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the tag
+ is not added.
+ """
+ rx_cmd_output = self.send_command(f"rx_vlan {'add' if add else 'rm'} {vlan} {port}")
+ if verify:
+ if (
+ "VLAN-filtering disabled" in rx_cmd_output
+ or "Invalid vlan_id" in rx_cmd_output
+ or "Bad arguments" in rx_cmd_output
+ ):
+ self._logger.debug(
+ f"""Failed to {'add' if add else 'remove'} tag {vlan}
+ port {port}: \n{rx_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to {'add' if add else 'remove'} tag {vlan} on port {port}."
+ )
+
+ def set_vlan_strip(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Enable or disable vlan stripping on the specified port.
+
+ Args:
+ port: The port number to use.
+ enable: If :data:`True`, will turn vlan stripping on, otherwise will turn off.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan stripping was enabled on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and stripping
+ fails to update.
+ """
+ strip_cmd_output = self.send_command(f"vlan set strip {'on' if enable else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if enable ^ (vlan_settings is not None and VLANOffloadFlag.STRIP in vlan_settings):
+ self._logger.debug(
+ f"""Failed to set strip {'on' if enable else 'off'}
+ port {port}: \n{strip_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set strip {'on' if enable else 'off'} port {port}."
+ )
+
+ @requires_stopped_ports
+ def tx_vlan_set(
+ self, port: int, enable: bool, vlan: int | None = None, verify: bool = True
+ ) -> None:
+ """Set hardware insertion of vlan tags in packets sent on a port.
+
+ Args:
+ port: The port number to use.
+ enable: Sets vlan tag insertion if :data:`True`, and resets if :data:`False`.
+ vlan: The vlan tag to insert if enable is :data:`True`.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ vlan insertion was enabled on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
+ tag is not set.
+ """
+ if enable:
+ tx_vlan_cmd_output = self.send_command(f"tx_vlan set {port} {vlan}")
+ if verify:
+ if (
+ "Please stop port" in tx_vlan_cmd_output
+ or "Invalid vlan_id" in tx_vlan_cmd_output
+ or "Invalid port" in tx_vlan_cmd_output
+ ):
+ self._logger.debug(
+ f"Failed to set vlan tag {vlan} on port {port}:\n{tx_vlan_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set vlan insertion tag {vlan} on port {port}."
+ )
+ else:
+ tx_vlan_cmd_output = self.send_command(f"tx_vlan reset {port}")
+ if verify:
+ if "Please stop port" in tx_vlan_cmd_output or "Invalid port" in tx_vlan_cmd_output:
+ self._logger.debug(
+ f"Failed to reset vlan insertion on port {port}: \n{tx_vlan_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to reset vlan insertion on port {port}."
+ )
+
+ def set_promisc(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Enable or disable promiscuous mode for the specified port.
+
+ Args:
+ port: Port number to use.
+ enable: If :data:`True`, turn promiscuous mode on, otherwise turn off.
+ verify: If :data:`True` an additional command will be sent to verify that
+ promiscuous mode is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and promiscuous mode
+ is not correctly set.
+ """
+ promisc_cmd_output = self.send_command(f"set promisc {port} {'on' if enable else 'off'}")
+ if verify:
+ stats = self.show_port_info(port_id=port)
+ if enable ^ stats.is_promiscuous_mode_enabled:
+ self._logger.debug(
+ f"Failed to set promiscuous mode on port {port}: \n{promisc_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set promiscuous mode on port {port}."
+ )
+
+ def set_verbose(self, level: int, verify: bool = True) -> None:
+ """Set debug verbosity level.
+
+ Args:
+ level: 0 - silent except for error
+ 1 - fully verbose except for Tx packets
+ 2 - fully verbose except for Rx packets
+ >2 - fully verbose
+ verify: If :data:`True` the command output will be scanned to verify that verbose level
+ is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and verbose level
+ is not correctly set.
+ """
+ verbose_cmd_output = self.send_command(f"set verbose {level}")
+ if verify:
+ if "Change verbose level" not in verbose_cmd_output:
+ self._logger.debug(
+ f"Failed to set verbose level to {level}: \n{verbose_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set verbose level to {level}."
+ )
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
--
2.44.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3] dts: add VLAN methods to testpmd shell
2024-10-08 17:15 ` Dean Marx
@ 2024-10-08 18:54 ` Dean Marx
2024-10-08 19:07 ` Dean Marx
1 sibling, 0 replies; 9+ messages in thread
From: Dean Marx @ 2024-10-08 18:54 UTC (permalink / raw)
To: probb, npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Added the following methods to testpmd shell class:
set vlan filter on/off, rx vlan add/rm,
set vlan strip on/off, tx vlan set/reset,
set promisc/verbose. Fixed a bug in the VLAN regex
used in testpmd shell flags.
Fixes: 61d5bc9bf974 ("dts: add port info command to testpmd shell")
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 189 ++++++++++++++++--
1 file changed, 172 insertions(+), 17 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 6da0dd477b..16b41a7814 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -118,7 +118,7 @@ def make_parser(cls) -> ParserFn:
r"strip (?P<STRIP>on|off), "
r"filter (?P<FILTER>on|off), "
r"extend (?P<EXTEND>on|off), "
- r"qinq strip (?P<QINQ_STRIP>on|off)$",
+ r"qinq strip (?P<QINQ_STRIP>on|off)",
re.MULTILINE,
named=True,
),
@@ -1428,22 +1428,6 @@ def __init__(
TestPmdParams(**app_params),
name,
)
- self._ports = None
-
- @property
- def ports(self) -> list[TestPmdPort]:
- """The ports of the instance.
-
- This caches the ports returned by :meth:`show_port_info_all`.
- To force an update of port information, execute :meth:`show_port_info_all` or
- :meth:`show_port_info`.
-
- Returns: The list of known testpmd ports.
- """
- if self._ports is None:
- return self.show_port_info_all()
- return self._ports
-
self.ports_started = not self._app_params.disable_device_start
self._ports = None
@@ -1788,6 +1772,177 @@ def extract_verbose_output(output: str) -> list[TestPmdVerbosePacket]:
out.append(TestPmdVerbosePacket.parse(f"{prev_header}\n{match.group('PACKET')}"))
return out
+ def set_vlan_filter(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Set vlan filter on.
+
+ Args:
+ port: The port number to enable VLAN filter on.
+ enable: Enable the filter on `port` if :data:`True`, otherwise disable it.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan filtering was set successfully.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the filter
+ fails to update.
+ """
+ filter_cmd_output = self.send_command(f"vlan set filter {'on' if enable else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if enable ^ (vlan_settings is not None and VLANOffloadFlag.FILTER in vlan_settings):
+ self._logger.debug(
+ f"""Failed to {'enable' if enable else 'disable'}
+ filter on port {port}: \n{filter_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"""Failed to {'enable' if enable else 'disable'}
+ filter on port {port}"""
+ )
+
+ def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool = True) -> None:
+ """Add specified vlan tag to the filter list on a port. Requires vlan filter to be on.
+
+ Args:
+ vlan: The vlan tag to add, should be within 1-1005.
+ port: The port number to add the tag on.
+ add: Adds the tag if :data:`True`, otherwise removes the tag.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ the vlan tag was added to the filter list on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the tag
+ is not added.
+ """
+ rx_cmd_output = self.send_command(f"rx_vlan {'add' if add else 'rm'} {vlan} {port}")
+ if verify:
+ if (
+ "VLAN-filtering disabled" in rx_cmd_output
+ or "Invalid vlan_id" in rx_cmd_output
+ or "Bad arguments" in rx_cmd_output
+ ):
+ self._logger.debug(
+ f"""Failed to {'add' if add else 'remove'} tag {vlan}
+ port {port}: \n{rx_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to {'add' if add else 'remove'} tag {vlan} on port {port}."
+ )
+
+ def set_vlan_strip(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Enable or disable vlan stripping on the specified port.
+
+ Args:
+ port: The port number to use.
+ enable: If :data:`True`, will turn vlan stripping on, otherwise will turn off.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan stripping was enabled on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and stripping
+ fails to update.
+ """
+ strip_cmd_output = self.send_command(f"vlan set strip {'on' if enable else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if enable ^ (vlan_settings is not None and VLANOffloadFlag.STRIP in vlan_settings):
+ self._logger.debug(
+ f"""Failed to set strip {'on' if enable else 'off'}
+ port {port}: \n{strip_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set strip {'on' if enable else 'off'} port {port}."
+ )
+
+ @requires_stopped_ports
+ def tx_vlan_set(
+ self, port: int, enable: bool, vlan: int | None = None, verify: bool = True
+ ) -> None:
+ """Set hardware insertion of vlan tags in packets sent on a port.
+
+ Args:
+ port: The port number to use.
+ enable: Sets vlan tag insertion if :data:`True`, and resets if :data:`False`.
+ vlan: The vlan tag to insert if enable is :data:`True`.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ vlan insertion was enabled on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
+ tag is not set.
+ """
+ if enable:
+ tx_vlan_cmd_output = self.send_command(f"tx_vlan set {port} {vlan}")
+ if verify:
+ if (
+ "Please stop port" in tx_vlan_cmd_output
+ or "Invalid vlan_id" in tx_vlan_cmd_output
+ or "Invalid port" in tx_vlan_cmd_output
+ ):
+ self._logger.debug(
+ f"Failed to set vlan tag {vlan} on port {port}:\n{tx_vlan_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set vlan insertion tag {vlan} on port {port}."
+ )
+ else:
+ tx_vlan_cmd_output = self.send_command(f"tx_vlan reset {port}")
+ if verify:
+ if "Please stop port" in tx_vlan_cmd_output or "Invalid port" in tx_vlan_cmd_output:
+ self._logger.debug(
+ f"Failed to reset vlan insertion on port {port}: \n{tx_vlan_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to reset vlan insertion on port {port}."
+ )
+
+ def set_promisc(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Enable or disable promiscuous mode for the specified port.
+
+ Args:
+ port: Port number to use.
+ enable: If :data:`True`, turn promiscuous mode on, otherwise turn off.
+ verify: If :data:`True` an additional command will be sent to verify that
+ promiscuous mode is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and promiscuous mode
+ is not correctly set.
+ """
+ promisc_cmd_output = self.send_command(f"set promisc {port} {'on' if enable else 'off'}")
+ if verify:
+ stats = self.show_port_info(port_id=port)
+ if enable ^ stats.is_promiscuous_mode_enabled:
+ self._logger.debug(
+ f"Failed to set promiscuous mode on port {port}: \n{promisc_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set promiscuous mode on port {port}."
+ )
+
+ def set_verbose(self, level: int, verify: bool = True) -> None:
+ """Set debug verbosity level.
+
+ Args:
+ level: 0 - silent except for error
+ 1 - fully verbose except for Tx packets
+ 2 - fully verbose except for Rx packets
+ >2 - fully verbose
+ verify: If :data:`True` the command output will be scanned to verify that verbose level
+ is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and verbose level
+ is not correctly set.
+ """
+ verbose_cmd_output = self.send_command(f"set verbose {level}")
+ if verify:
+ if "Change verbose level" not in verbose_cmd_output:
+ self._logger.debug(
+ f"Failed to set verbose level to {level}: \n{verbose_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set verbose level to {level}."
+ )
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
--
2.44.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3] dts: add VLAN methods to testpmd shell
2024-10-08 17:15 ` Dean Marx
2024-10-08 18:54 ` [PATCH v3] " Dean Marx
@ 2024-10-08 19:07 ` Dean Marx
2024-10-10 17:33 ` Patrick Robb
1 sibling, 1 reply; 9+ messages in thread
From: Dean Marx @ 2024-10-08 19:07 UTC (permalink / raw)
To: probb, npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Added the following methods to testpmd shell class:
set vlan filter on/off, rx vlan add/rm,
set vlan strip on/off, tx vlan set/reset,
set promisc/verbose. Fixed a bug in the VLAN regex
used in testpmd shell flags.
Fixes: 61d5bc9bf974 ("dts: add port info command to testpmd shell")
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 173 +++++++++++++++++-
1 file changed, 172 insertions(+), 1 deletion(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 6da0dd477b..dc1bef431d 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -118,7 +118,7 @@ def make_parser(cls) -> ParserFn:
r"strip (?P<STRIP>on|off), "
r"filter (?P<FILTER>on|off), "
r"extend (?P<EXTEND>on|off), "
- r"qinq strip (?P<QINQ_STRIP>on|off)$",
+ r"qinq strip (?P<QINQ_STRIP>on|off)",
re.MULTILINE,
named=True,
),
@@ -1788,6 +1788,177 @@ def extract_verbose_output(output: str) -> list[TestPmdVerbosePacket]:
out.append(TestPmdVerbosePacket.parse(f"{prev_header}\n{match.group('PACKET')}"))
return out
+ def set_vlan_filter(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Set vlan filter on.
+
+ Args:
+ port: The port number to enable VLAN filter on.
+ enable: Enable the filter on `port` if :data:`True`, otherwise disable it.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan filtering was set successfully.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the filter
+ fails to update.
+ """
+ filter_cmd_output = self.send_command(f"vlan set filter {'on' if enable else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if enable ^ (vlan_settings is not None and VLANOffloadFlag.FILTER in vlan_settings):
+ self._logger.debug(
+ f"""Failed to {'enable' if enable else 'disable'}
+ filter on port {port}: \n{filter_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"""Failed to {'enable' if enable else 'disable'}
+ filter on port {port}"""
+ )
+
+ def rx_vlan(self, vlan: int, port: int, add: bool, verify: bool = True) -> None:
+ """Add specified vlan tag to the filter list on a port. Requires vlan filter to be on.
+
+ Args:
+ vlan: The vlan tag to add, should be within 1-1005.
+ port: The port number to add the tag on.
+ add: Adds the tag if :data:`True`, otherwise removes the tag.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ the vlan tag was added to the filter list on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the tag
+ is not added.
+ """
+ rx_cmd_output = self.send_command(f"rx_vlan {'add' if add else 'rm'} {vlan} {port}")
+ if verify:
+ if (
+ "VLAN-filtering disabled" in rx_cmd_output
+ or "Invalid vlan_id" in rx_cmd_output
+ or "Bad arguments" in rx_cmd_output
+ ):
+ self._logger.debug(
+ f"""Failed to {'add' if add else 'remove'} tag {vlan}
+ port {port}: \n{rx_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to {'add' if add else 'remove'} tag {vlan} on port {port}."
+ )
+
+ def set_vlan_strip(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Enable or disable vlan stripping on the specified port.
+
+ Args:
+ port: The port number to use.
+ enable: If :data:`True`, will turn vlan stripping on, otherwise will turn off.
+ verify: If :data:`True`, the output of the command and show port info
+ is scanned to verify that vlan stripping was enabled on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and stripping
+ fails to update.
+ """
+ strip_cmd_output = self.send_command(f"vlan set strip {'on' if enable else 'off'} {port}")
+ if verify:
+ vlan_settings = self.show_port_info(port_id=port).vlan_offload
+ if enable ^ (vlan_settings is not None and VLANOffloadFlag.STRIP in vlan_settings):
+ self._logger.debug(
+ f"""Failed to set strip {'on' if enable else 'off'}
+ port {port}: \n{strip_cmd_output}"""
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set strip {'on' if enable else 'off'} port {port}."
+ )
+
+ @requires_stopped_ports
+ def tx_vlan_set(
+ self, port: int, enable: bool, vlan: int | None = None, verify: bool = True
+ ) -> None:
+ """Set hardware insertion of vlan tags in packets sent on a port.
+
+ Args:
+ port: The port number to use.
+ enable: Sets vlan tag insertion if :data:`True`, and resets if :data:`False`.
+ vlan: The vlan tag to insert if enable is :data:`True`.
+ verify: If :data:`True`, the output of the command is scanned to verify that
+ vlan insertion was enabled on the specified port.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the insertion
+ tag is not set.
+ """
+ if enable:
+ tx_vlan_cmd_output = self.send_command(f"tx_vlan set {port} {vlan}")
+ if verify:
+ if (
+ "Please stop port" in tx_vlan_cmd_output
+ or "Invalid vlan_id" in tx_vlan_cmd_output
+ or "Invalid port" in tx_vlan_cmd_output
+ ):
+ self._logger.debug(
+ f"Failed to set vlan tag {vlan} on port {port}:\n{tx_vlan_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set vlan insertion tag {vlan} on port {port}."
+ )
+ else:
+ tx_vlan_cmd_output = self.send_command(f"tx_vlan reset {port}")
+ if verify:
+ if "Please stop port" in tx_vlan_cmd_output or "Invalid port" in tx_vlan_cmd_output:
+ self._logger.debug(
+ f"Failed to reset vlan insertion on port {port}: \n{tx_vlan_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to reset vlan insertion on port {port}."
+ )
+
+ def set_promisc(self, port: int, enable: bool, verify: bool = True) -> None:
+ """Enable or disable promiscuous mode for the specified port.
+
+ Args:
+ port: Port number to use.
+ enable: If :data:`True`, turn promiscuous mode on, otherwise turn off.
+ verify: If :data:`True` an additional command will be sent to verify that
+ promiscuous mode is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and promiscuous mode
+ is not correctly set.
+ """
+ promisc_cmd_output = self.send_command(f"set promisc {port} {'on' if enable else 'off'}")
+ if verify:
+ stats = self.show_port_info(port_id=port)
+ if enable ^ stats.is_promiscuous_mode_enabled:
+ self._logger.debug(
+ f"Failed to set promiscuous mode on port {port}: \n{promisc_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set promiscuous mode on port {port}."
+ )
+
+ def set_verbose(self, level: int, verify: bool = True) -> None:
+ """Set debug verbosity level.
+
+ Args:
+ level: 0 - silent except for error
+ 1 - fully verbose except for Tx packets
+ 2 - fully verbose except for Rx packets
+ >2 - fully verbose
+ verify: If :data:`True` the command output will be scanned to verify that verbose level
+ is properly set. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and verbose level
+ is not correctly set.
+ """
+ verbose_cmd_output = self.send_command(f"set verbose {level}")
+ if verify:
+ if "Change verbose level" not in verbose_cmd_output:
+ self._logger.debug(
+ f"Failed to set verbose level to {level}: \n{verbose_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Testpmd failed to set verbose level to {level}."
+ )
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
--
2.44.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] dts: add VLAN methods to testpmd shell
2024-10-08 19:07 ` Dean Marx
@ 2024-10-10 17:33 ` Patrick Robb
0 siblings, 0 replies; 9+ messages in thread
From: Patrick Robb @ 2024-10-10 17:33 UTC (permalink / raw)
To: Dean Marx
Cc: npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek, dev
[-- Attachment #1: Type: text/plain, Size: 29 bytes --]
Applied to next-dts, thanks!
[-- Attachment #2: Type: text/html, Size: 50 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2024-10-10 17:34 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-09-11 17:38 [PATCH v1] dts: add VLAN methods to testpmd shell Dean Marx
2024-09-12 2:17 ` Patrick Robb
2024-09-18 19:41 ` [PATCH v2] " Dean Marx
2024-09-20 18:33 ` Jeremy Spewock
2024-09-24 13:56 ` Juraj Linkeš
2024-10-08 17:15 ` Dean Marx
2024-10-08 18:54 ` [PATCH v3] " Dean Marx
2024-10-08 19:07 ` Dean Marx
2024-10-10 17:33 ` Patrick Robb
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).