On Mon, Jun 10, 2024 at 11:03 AM Juraj Linkeš
<juraj.lin...@pantheon.tech> wrote:
>
>
>
> On 5. 6. 2024 23:31, jspew...@iol.unh.edu wrote:
> > From: Jeremy Spewock <jspew...@iol.unh.edu>
> >
> > There are methods within DTS currently that support updating the MTU of
> > ports on a node, but the methods for doing this in a linux session rely
> > on the ip command and the port being bound to the kernel driver. Since
> > test suites are run while bound to the driver for DPDK, there needs to
> > be a way to modify the value while bound to said driver as well. This is
> > done by using testpmd to modify the MTU.
> >
> > Signed-off-by: Jeremy Spewock <jspew...@iol.unh.edu>
> > ---
> >   .../remote_session/interactive_shell.py       | 14 +++-
> >   dts/framework/remote_session/testpmd_shell.py | 76 ++++++++++++++++++-
> >   2 files changed, 86 insertions(+), 4 deletions(-)
> >
> > diff --git a/dts/framework/remote_session/interactive_shell.py 
> > b/dts/framework/remote_session/interactive_shell.py
> > index 6dee7ebce0..34d1acf439 100644
> > --- a/dts/framework/remote_session/interactive_shell.py
> > +++ b/dts/framework/remote_session/interactive_shell.py
> > @@ -139,7 +139,9 @@ def _start_application(self, get_privileged_command: 
> > Callable[[str], str] | None
> >               raise InteractiveCommandExecutionError("Failed to start 
> > application.")
> >           self._ssh_channel.settimeout(self._timeout)
> >
> > -    def send_command(self, command: str, prompt: str | None = None) -> str:
> > +    def send_command(
> > +        self, command: str, prompt: str | None = None, print_to_debug: 
> > bool = False
> > +    ) -> str:
>
> As I mentioned in v2, this really should be in a separate patch, as it
> affects other parts of the code and the solution should be designed with
> that in mind.

Ack.

>
> >           """Send `command` and get all output before the expected ending 
> > string.
> >
> >           Lines that expect input are not included in the stdout buffer, so 
> > they cannot
> > @@ -155,6 +157,10 @@ def send_command(self, command: str, prompt: str | 
> > None = None) -> str:
> >               command: The command to send.
> >               prompt: After sending the command, `send_command` will be 
> > expecting this string.
> >                   If :data:`None`, will use the class's default prompt.
> > +            print_to_debug: If :data:`True` the logging message that 
> > displays what command is
> > +                being sent prior to sending it will be logged at the debug 
> > level instead of the
> > +                info level. Useful when a single action requires multiple 
> > commands to complete to
> > +                avoid clutter in the logs.
> >
> >           Returns:
> >               All output in the buffer before expected string.
> > @@ -163,7 +169,11 @@ def send_command(self, command: str, prompt: str | 
> > None = None) -> str:
> >               raise InteractiveCommandExecutionError(
> >                   f"Cannot send command {command} to application because 
> > the shell is not running."
> >               )
> > -        self._logger.info(f"Sending: '{command}'")
> > +        log_message = f"Sending: '{command}'"
> > +        if print_to_debug:
> > +            self._logger.debug(log_message)
> > +        else:
> > +            self._logger.info(log_message)
> >           if prompt is None:
> >               prompt = self._default_prompt
> >           self._stdin.write(f"{command}{self._command_extra_chars}\n")
> > diff --git a/dts/framework/remote_session/testpmd_shell.py 
> > b/dts/framework/remote_session/testpmd_shell.py
> > index ca30aac264..f2fa842b7f 100644
> > --- a/dts/framework/remote_session/testpmd_shell.py
> > +++ b/dts/framework/remote_session/testpmd_shell.py
> > @@ -135,10 +135,11 @@ def start(self, verify: bool = True) -> None:
> >               InteractiveCommandExecutionError: If `verify` is :data:`True` 
> > and forwarding fails to
> >                   start or ports fail to come up.
> >           """
> > -        self.send_command("start")
> > +        self._logger.info('Starting packet forwarding and waiting for port 
> > links to be "up".')
> > +        self.send_command("start", print_to_debug=True)
> >           if verify:
> >               # If forwarding was already started, sending "start" again 
> > should tell us
> > -            start_cmd_output = self.send_command("start")
> > +            start_cmd_output = self.send_command("start", 
> > print_to_debug=True)
> >               if "Packet forwarding already started" not in 
> > start_cmd_output:
> >                   self._logger.debug(f"Failed to start packet forwarding: 
> > \n{start_cmd_output}")
> >                   raise InteractiveCommandExecutionError("Testpmd failed to 
> > start packet forwarding.")
> > @@ -227,6 +228,77 @@ def set_forward_mode(self, mode: 
> > TestPmdForwardingModes, verify: bool = True):
> >                   f"Test pmd failed to set fwd mode to {mode.value}"
> >               )
> >
> > +    def _stop_port(self, port_id: int, verify: bool = True) -> None:
> > +        """Stop port `port_id` in testpmd.
>
> Either "Stop `port_id` in testpmd." or "Stop port with `port_id` in
> testpmd.". I like the latter more.

Ack.

>
> > +
> > +        Depending on the PMD, the port may need to be stopped before 
> > configuration can take place.
> > +        This method wraps the command needed to properly stop ports and 
> > take their link down.
> > +
> > +        Args:
> > +            port_id: ID of the port to take down.
> > +            verify: If :data:`True` the output will be scanned in an 
> > attempt to verify that the
> > +                stopping of ports was successful. Defaults to True.
> > +
> > +        Raises:
> > +            InteractiveCommandExecutionError: If `verify` is :data:`True` 
> > and the port did not
> > +                successfully stop.
> > +        """
> > +        stop_port_output = self.send_command(f"port stop {port_id}", 
> > print_to_debug=True)
> > +        if verify and ("Done" not in stop_port_output):
> > +            self._logger.debug(f"Failed to stop port {port_id}. Output 
> > was:\n{stop_port_output}")
> > +            raise InteractiveCommandExecutionError(f"Test pmd failed to 
> > stop port {port_id}.")
> > +
> > +    def _start_port(self, port_id: int, verify: bool = True) -> None:
> > +        """Start port `port_id` in testpmd.
> > +
> > +        Because the port may need to be stopped to make some configuration 
> > changes, it naturally
> > +        follows that it will need to be started again once those changes 
> > have been made.
> > +
> > +        Args:
> > +            port_id: ID of the port to start.
> > +            verify: If :data:`True` the output will be scanned in an 
> > attempt to verify that the
> > +                port came back up without error. Defaults to True.
> > +
> > +        Raises:
> > +            InteractiveCommandExecutionError: If `verify` is :data:`True` 
> > and the port did not come
> > +                back up.
> > +        """
> > +        start_port_output = self.send_command(f"port start {port_id}", 
> > print_to_debug=True)
> > +        if verify and ("Done" not in start_port_output):
> > +            self._logger.debug(f"Failed to start port {port_id}. Output 
> > was:\n{start_port_output}")
> > +            raise InteractiveCommandExecutionError(f"Test pmd failed to 
> > start port {port_id}.")
> > +
> > +    def set_port_mtu(self, port_id: int, mtu: int, verify: bool = True) -> 
> > None:
> > +        """Change the MTU of a port using testpmd.
> > +
> > +        Some PMDs require that the port be stopped before changing the 
> > MTU, and it does no harm to
> > +        stop the port before configuring in cases where it isn't required, 
> > so we first stop ports,
> > +        then update the MTU, then start the ports again afterwards.
> > +
> > +        Args:
> > +            port_id: ID of the port to adjust the MTU on.
> > +            mtu: Desired value for the MTU to be set to.
> > +            verify: If `verify` is :data:`True` then the output will be 
> > scanned in an attempt to
> > +                verify that the mtu was properly set on the port. Defaults 
> > to True.
>
> The second instance of True should also be :data:`True`.

Ugh, good catch! My IDE generates boilerplate for them for me and I
caught this in another method I added here, but I guess I missed this
one.

>
> > +
> > +        Raises:
> > +            InteractiveCommandExecutionError: If `verify` is :data:`True` 
> > and the MTU was not
> > +                properly updated on the port matching `port_id`.
> > +        """
> > +        self._logger.info(f"Changing MTU of port {port_id} to be {mtu}")
> > +        self._stop_port(port_id, verify)
> > +        set_mtu_output = self.send_command(f"port config mtu {port_id} 
> > {mtu}", print_to_debug=True)
> > +        self._start_port(port_id, verify)
>
> Would making _stop_port and _start_port a decorator work? Can we do the
> verification even if the port is stopped?

I like this idea a lot. I just checked and it looks like you can do
the validation while the port is stopped, so I'll make this change.


>
> > +        if verify and (
> > +            f"MTU: {mtu}" not in self.send_command(f"show port info 
> > {port_id}", print_to_debug=True)
> > +        ):
> > +            self._logger.debug(
> > +                f"Failed to set mtu to {mtu} on port {port_id}." f" Output 
> > was:\n{set_mtu_output}"
> > +            )
> > +            raise InteractiveCommandExecutionError(
> > +                f"Test pmd failed to update mtu of port {port_id} to {mtu}"
> > +            )
> > +
> >       def _close(self) -> None:
> >           """Overrides :meth:`~.interactive_shell.close`."""
> >           self.send_command("quit", "Bye...")

Reply via email to