[v3] dts: add flow rule dataclass to testpmd shell

Message ID 20240813144107.6195-1-dmarx@iol.unh.edu (mailing list archive)
State Superseded, archived
Delegated to: Juraj Linkeš
Headers
Series [v3] dts: add flow rule dataclass to testpmd shell |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/github-robot: build success github build: passed
ci/iol-marvell-Functional success Functional Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-sample-apps-testing success Testing PASS
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-unit-arm64-testing success Testing PASS
ci/iol-compile-arm64-testing success Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS
ci/intel-Functional success Functional PASS

Commit Message

Dean Marx Aug. 13, 2024, 2:41 p.m. UTC
add dataclass for passing in flow rule creation arguments, as well as a
__str__ method for converting to a sendable testpmd command. Add
flow_create method to TestPmdShell class for initializing flow rules.

Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
 dts/framework/remote_session/testpmd_shell.py | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)
  

Comments

Juraj Linkeš Sept. 25, 2024, 8:17 a.m. UTC | #1
On 13. 8. 2024 16:41, Dean Marx wrote:
> add dataclass for passing in flow rule creation arguments, as well as a

Capitalize please.

> __str__ method for converting to a sendable testpmd command. Add
> flow_create method to TestPmdShell class for initializing flow rules.
> 
> Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
> ---
>   dts/framework/remote_session/testpmd_shell.py | 57 +++++++++++++++++++
>   1 file changed, 57 insertions(+)
> 
> diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
> index 43e9f56517..17b9c7811d 100644
> --- a/dts/framework/remote_session/testpmd_shell.py
> +++ b/dts/framework/remote_session/testpmd_shell.py
> @@ -577,6 +577,44 @@ class TestPmdPortStats(TextParser):
>       tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
>   
>   
> +@dataclass
> +class FlowRule:
> +    """Dataclass for setting flow rule parameters."""
> +
> +    #:
> +    port_id: int
> +    #:
> +    ingress: bool
> +    #:
> +    pattern: str
> +    #:
> +    actions: str
> +
> +    #:
> +    group_id: int | None = None
> +    #:
> +    priority_level: int | None = None
> +    #:
> +    user_id: int | None = None
> +
> +    def __str__(self) -> str:
> +        """Returns the string representation of a flow_func instance.
> +
> +        In this case, a properly formatted flow create command that can be sent to testpmd.

I think it would be beneficial for a complicated command like this to 
add the actual format of the command as returned by testpmd, which 
should be (according to app/test-pmd/cmdline.c):

flow create {port_id}
  [group {group_id}] [priority {level}]
  [ingress] [egress]
  pattern {item} [/ {item} [...]] / end
  actions {action} [/ {action} [...]] / end

Looking at this, there could be multiple patterns and actions. Maybe we 
should add classes for these two as well - what do the patterns and 
actions look like?

> +        """
> +        ret = f"flow create {self.port_id} "
> +        if self.group_id is not None:
> +            ret += f"group {self.group_id} "
> +        if self.priority_level is not None:
> +            ret += f"priority {self.priority_level} "
> +        ret += "ingress " if self.ingress else "egress "
> +        if self.user_id is not None:
> +            ret += f"user_id {self.user_id} "
> +        ret += f"pattern {self.pattern} / end "
> +        ret += f"actions {self.actions} / end"
> +        return ret
> +
> +
>   class TestPmdShell(DPDKShell):
>       """Testpmd interactive shell.
>   
> @@ -806,6 +844,25 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
>   
>           return TestPmdPortStats.parse(output)
>   
> +    def flow_create(self, cmd: FlowRule, verify: bool = True) -> None:

Do we also need to add a method for deleting (I guess it would be called 
destroying per the command) the rule? Or any other flow rule commands? 
We could expand the test suite (I assume we're adding this because of a 
test suite) if such tests make sense.

> +        """Creates a flow rule in the testpmd session.
> +
> +        Args:
> +            cmd: String from flow_func instance to send as a flow rule.

The cmd docstring has not been updated. I'd also rename cmd to flow_rule.

> +            verify: If :data:`True`, the output of the command is scanned
> +            to ensure the flow rule was created successfully.
> +
> +        Raises:
> +            InteractiveCommandExecutionError: If flow rule is invalid.
> +        """
> +        flow_output = self.send_command(str(cmd))
> +        if verify:
> +            if "created" not in flow_output:
> +                self._logger.debug(f"Failed to create flow rule:\n{flow_output}")
> +                raise InteractiveCommandExecutionError(
> +                    f"Failed to create flow rule:\n{flow_output}"
> +                )
> +
>       def _close(self) -> None:
>           """Overrides :meth:`~.interactive_shell.close`."""
>           self.stop()
  

Patch

diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 43e9f56517..17b9c7811d 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -577,6 +577,44 @@  class TestPmdPortStats(TextParser):
     tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)"))
 
 
+@dataclass
+class FlowRule:
+    """Dataclass for setting flow rule parameters."""
+
+    #:
+    port_id: int
+    #:
+    ingress: bool
+    #:
+    pattern: str
+    #:
+    actions: str
+
+    #:
+    group_id: int | None = None
+    #:
+    priority_level: int | None = None
+    #:
+    user_id: int | None = None
+
+    def __str__(self) -> str:
+        """Returns the string representation of a flow_func instance.
+
+        In this case, a properly formatted flow create command that can be sent to testpmd.
+        """
+        ret = f"flow create {self.port_id} "
+        if self.group_id is not None:
+            ret += f"group {self.group_id} "
+        if self.priority_level is not None:
+            ret += f"priority {self.priority_level} "
+        ret += "ingress " if self.ingress else "egress "
+        if self.user_id is not None:
+            ret += f"user_id {self.user_id} "
+        ret += f"pattern {self.pattern} / end "
+        ret += f"actions {self.actions} / end"
+        return ret
+
+
 class TestPmdShell(DPDKShell):
     """Testpmd interactive shell.
 
@@ -806,6 +844,25 @@  def show_port_stats(self, port_id: int) -> TestPmdPortStats:
 
         return TestPmdPortStats.parse(output)
 
+    def flow_create(self, cmd: FlowRule, verify: bool = True) -> None:
+        """Creates a flow rule in the testpmd session.
+
+        Args:
+            cmd: String from flow_func instance to send as a flow rule.
+            verify: If :data:`True`, the output of the command is scanned
+            to ensure the flow rule was created successfully.
+
+        Raises:
+            InteractiveCommandExecutionError: If flow rule is invalid.
+        """
+        flow_output = self.send_command(str(cmd))
+        if verify:
+            if "created" not in flow_output:
+                self._logger.debug(f"Failed to create flow rule:\n{flow_output}")
+                raise InteractiveCommandExecutionError(
+                    f"Failed to create flow rule:\n{flow_output}"
+                )
+
     def _close(self) -> None:
         """Overrides :meth:`~.interactive_shell.close`."""
         self.stop()