From: Patrick Robb <probb@iol.unh.edu>
To: jspewock@iol.unh.edu
Cc: yoan.picchi@foss.arm.com, Honnappa.Nagarahalli@arm.com,
paul.szczepanek@arm.com, juraj.linkes@pantheon.tech,
Luca.Vizzarro@arm.com, wathsala.vithanage@arm.com,
thomas@monjalon.net, dev@dpdk.org
Subject: Re: [PATCH v1 2/4] dts: add context manager for interactive shells
Date: Wed, 22 May 2024 09:53:41 -0400 [thread overview]
Message-ID: <CAJvnSUBsrP8ffsd=swJyPdM_089jm1WSc-VTDG=Zf=HmujpqFQ@mail.gmail.com> (raw)
In-Reply-To: <20240514201436.2496-3-jspewock@iol.unh.edu>
Reviewed-by: Patrick Robb <probb@iol.unh.edu>
I don't have any comments beyond Luca's suggestions, but saw the typo below.
On Tue, May 14, 2024 at 4:15 PM <jspewock@iol.unh.edu> wrote:
> + def __exit__(self, type: BaseException, value: BaseException, traceback: TracebackType) -> None:
> + """Exit the context block.
> +
> + Upon exiting a context block with this class, we want to ensure that the instance of the
> + application is explicitly closed and properly cleaned up using it's close method. Note that
it's -> its
On Tue, May 14, 2024 at 4:15 PM <jspewock@iol.unh.edu> wrote:
>
> From: Jeremy Spewock <jspewock@iol.unh.edu>
>
> Interactive shells are managed in a way currently where they are closed
> and cleaned up at the time of garbage collection. Due to there being no
> guarantee of when this garbage collection happens in Python, there is no
> way to consistently know when an application will be closed without
> manually closing the application yourself when you are done with it.
> This doesn't cause a problem in cases where you can start another
> instance of the same application multiple times on a server, but this
> isn't the case for primary applications in DPDK. The introduction of
> primary applications, such as testpmd, adds a need for knowing previous
> instances of the application have been stopped and cleaned up before
> starting a new one, which the garbage collector does not provide.
>
> To solve this problem, a new class is added which acts as a wrapper
> around the interactive shell that enforces that instances of the
> application be managed using a context manager. Using a context manager
> guarantees that once you leave the scope of the block where the
> application is being used for any reason, the application will be closed
> immediately. This avoids the possibility of the shell not being closed
> due to an exception being raised or user error.
>
> depends-on: patch-139227 ("dts: skip test cases based on capabilities")
>
> Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
> ---
> .../critical_interactive_shell.py | 98 +++++++++++++++++++
> .../remote_session/interactive_shell.py | 13 ++-
> dts/framework/remote_session/testpmd_shell.py | 4 +-
> dts/framework/testbed_model/sut_node.py | 8 +-
> dts/tests/TestSuite_pmd_buffer_scatter.py | 28 +++---
> dts/tests/TestSuite_smoke_tests.py | 3 +-
> 6 files changed, 130 insertions(+), 24 deletions(-)
> create mode 100644 dts/framework/remote_session/critical_interactive_shell.py
>
> diff --git a/dts/framework/remote_session/critical_interactive_shell.py b/dts/framework/remote_session/critical_interactive_shell.py
> new file mode 100644
> index 0000000000..d61b203954
> --- /dev/null
> +++ b/dts/framework/remote_session/critical_interactive_shell.py
> @@ -0,0 +1,98 @@
> +r"""Wrapper around :class:`~.interactive_shell.InteractiveShell` that handles critical applications.
> +
> +Critical applications are defined as applications that require explicit clean-up before another
> +instance of some application can be started. In DPDK these are referred to as "primary
> +applications" and these applications take out a lock which stops other primary applications from
> +running. Much like :class:`~.interactive_shell.InteractiveShell`\s,
> +:class:`CriticalInteractiveShell` is meant to be extended by subclasses that implement application
> +specific functionality and should never be instantiated directly.
> +"""
> +
> +from types import TracebackType
> +from typing import Callable, TypeVar
> +
> +from paramiko import SSHClient # type: ignore[import]
> +
> +from framework.logger import DTSLogger
> +from framework.settings import SETTINGS
> +
> +from .interactive_shell import InteractiveShell
> +
> +CriticalInteractiveShellType = TypeVar(
> + "CriticalInteractiveShellType", bound="CriticalInteractiveShell"
> +)
> +
> +
> +class CriticalInteractiveShell(InteractiveShell):
> + """The base class for interactive critical applications.
> +
> + This class is a wrapper around :class:`~.interactive_shell.InteractiveShell` and should always
> + implement the exact same functionality with the primary difference being how the application
> + is started and stopped. In contrast to normal interactive shells, this class does not start the
> + application upon initialization of the class. Instead, the application is handled through a
> + context manager. This allows for more explicit starting and stopping of the application, and
> + more guarantees for when the application is cleaned up which are not present with normal
> + interactive shells that get cleaned up upon garbage collection.
> + """
> +
> + _get_priviledged_command: Callable[[str], str] | None
> +
> + def __init__(
> + self,
> + interactive_session: SSHClient,
> + logger: DTSLogger,
> + get_privileged_command: Callable[[str], str] | None,
> + app_args: str = "",
> + timeout: float = SETTINGS.timeout,
> + ) -> None:
> + """Store parameters for creating an interactive shell, but do not start the application.
> +
> + Note that this method also does not create the channel for the application, as this is
> + something that isn't needed until the application starts.
> +
> + Args:
> + interactive_session: The SSH session dedicated to interactive shells.
> + logger: The logger instance this session will use.
> + get_privileged_command: A method for modifying a command to allow it to use
> + elevated privileges. If :data:`None`, the application will not be started
> + with elevated privileges.
> + app_args: The command line arguments to be passed to the application on startup.
> + timeout: The timeout used for the SSH channel that is dedicated to this interactive
> + shell. This timeout is for collecting output, so if reading from the buffer
> + and no output is gathered within the timeout, an exception is thrown. The default
> + value for this argument may be modified using the :option:`--timeout` command-line
> + argument or the :envvar:`DTS_TIMEOUT` environment variable.
> + """
> + self._interactive_session = interactive_session
> + self._logger = logger
> + self._timeout = timeout
> + self._app_args = app_args
> + self._get_priviledged_command = get_privileged_command
> +
> + def __enter__(self: CriticalInteractiveShellType) -> CriticalInteractiveShellType:
> + """Enter the context block.
> +
> + Upon entering a context block with this class, the desired behavior is to create the
> + channel for the application to use, and then start the application.
> +
> + Returns:
> + Reference to the object for the application after it has been started.
> + """
> + self._init_channel()
> + self._start_application(self._get_priviledged_command)
> + return self
> +
> + def __exit__(self, type: BaseException, value: BaseException, traceback: TracebackType) -> None:
> + """Exit the context block.
> +
> + Upon exiting a context block with this class, we want to ensure that the instance of the
> + application is explicitly closed and properly cleaned up using it's close method. Note that
> + because this method returns :data:`None` if an exception was raised within the block, it is
> + not handled and will be re-raised after the application is closed.
> +
> + Args:
> + type: Type of exception that was thrown in the context block if there was one.
> + value: Value of the exception thrown in the context block if there was one.
> + traceback: Traceback of the exception thrown in the context block if there was one.
> + """
> + self.close()
> diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
> index d1a9d8a6d2..08b8ba6a3e 100644
> --- a/dts/framework/remote_session/interactive_shell.py
> +++ b/dts/framework/remote_session/interactive_shell.py
> @@ -89,16 +89,19 @@ def __init__(
> and no output is gathered within the timeout, an exception is thrown.
> """
> self._interactive_session = interactive_session
> - self._ssh_channel = self._interactive_session.invoke_shell()
> - self._stdin = self._ssh_channel.makefile_stdin("w")
> - self._stdout = self._ssh_channel.makefile("r")
> - self._ssh_channel.settimeout(timeout)
> - self._ssh_channel.set_combine_stderr(True) # combines stdout and stderr streams
> self._logger = logger
> self._timeout = timeout
> self._app_args = app_args
> + self._init_channel()
> self._start_application(get_privileged_command)
>
> + def _init_channel(self):
> + self._ssh_channel = self._interactive_session.invoke_shell()
> + self._stdin = self._ssh_channel.makefile_stdin("w")
> + self._stdout = self._ssh_channel.makefile("r")
> + self._ssh_channel.settimeout(self._timeout)
> + self._ssh_channel.set_combine_stderr(True) # combines stdout and stderr streams
> +
> def _start_application(self, get_privileged_command: Callable[[str], str] | None) -> None:
> """Starts a new interactive application based on the path to the app.
>
> diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
> index cb4642bf3d..33b3e7c5a3 100644
> --- a/dts/framework/remote_session/testpmd_shell.py
> +++ b/dts/framework/remote_session/testpmd_shell.py
> @@ -26,7 +26,7 @@
> from framework.settings import SETTINGS
> from framework.utils import StrEnum
>
> -from .interactive_shell import InteractiveShell
> +from .critical_interactive_shell import CriticalInteractiveShell
>
>
> class TestPmdDevice(object):
> @@ -82,7 +82,7 @@ class TestPmdForwardingModes(StrEnum):
> recycle_mbufs = auto()
>
>
> -class TestPmdShell(InteractiveShell):
> +class TestPmdShell(CriticalInteractiveShell):
> """Testpmd interactive shell.
>
> The testpmd shell users should never use
> diff --git a/dts/framework/testbed_model/sut_node.py b/dts/framework/testbed_model/sut_node.py
> index 1fb536735d..7dd39fd735 100644
> --- a/dts/framework/testbed_model/sut_node.py
> +++ b/dts/framework/testbed_model/sut_node.py
> @@ -243,10 +243,10 @@ def get_supported_capabilities(
> unsupported_capas: set[NicCapability] = set()
> self._logger.debug(f"Checking which capabilities from {capabilities} NIC are supported.")
> testpmd_shell = self.create_interactive_shell(TestPmdShell, privileged=True)
> - for capability in capabilities:
> - if capability not in supported_capas or capability not in unsupported_capas:
> - capability.value(testpmd_shell, supported_capas, unsupported_capas)
> - del testpmd_shell
> + with testpmd_shell as running_testpmd:
> + for capability in capabilities:
> + if capability not in supported_capas or capability not in unsupported_capas:
> + capability.value(running_testpmd, supported_capas, unsupported_capas)
> return supported_capas
>
> def _set_up_build_target(self, build_target_config: BuildTargetConfiguration) -> None:
> diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py
> index 3701c47408..41f6090a7e 100644
> --- a/dts/tests/TestSuite_pmd_buffer_scatter.py
> +++ b/dts/tests/TestSuite_pmd_buffer_scatter.py
> @@ -101,7 +101,7 @@ def pmd_scatter(self, mbsize: int) -> None:
> Test:
> Start testpmd and run functional test with preset mbsize.
> """
> - testpmd = self.sut_node.create_interactive_shell(
> + testpmd_shell = self.sut_node.create_interactive_shell(
> TestPmdShell,
> app_parameters=(
> "--mbcache=200 "
> @@ -112,17 +112,21 @@ def pmd_scatter(self, mbsize: int) -> None:
> ),
> privileged=True,
> )
> - testpmd.set_forward_mode(TestPmdForwardingModes.mac)
> - testpmd.start()
> -
> - for offset in [-1, 0, 1, 4, 5]:
> - recv_payload = self.scatter_pktgen_send_packet(mbsize + offset)
> - self._logger.debug(f"Payload of scattered packet after forwarding: \n{recv_payload}")
> - self.verify(
> - ("58 " * 8).strip() in recv_payload,
> - f"Payload of scattered packet did not match expected payload with offset {offset}.",
> - )
> - testpmd.stop()
> + with testpmd_shell as testpmd:
> + testpmd.set_forward_mode(TestPmdForwardingModes.mac)
> + testpmd.start()
> +
> + for offset in [-1, 0, 1, 4, 5]:
> + recv_payload = self.scatter_pktgen_send_packet(mbsize + offset)
> + self._logger.debug(
> + f"Payload of scattered packet after forwarding: \n{recv_payload}"
> + )
> + self.verify(
> + ("58 " * 8).strip() in recv_payload,
> + "Payload of scattered packet did not match expected payload with offset "
> + f"{offset}.",
> + )
> + testpmd.stop()
>
> def test_scatter_mbuf_2048(self) -> None:
> """Run the :meth:`pmd_scatter` test with `mbsize` set to 2048."""
> diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
> index a553e89662..360e64eb5a 100644
> --- a/dts/tests/TestSuite_smoke_tests.py
> +++ b/dts/tests/TestSuite_smoke_tests.py
> @@ -100,7 +100,8 @@ def test_devices_listed_in_testpmd(self) -> None:
> List all devices found in testpmd and verify the configured devices are among them.
> """
> testpmd_driver = self.sut_node.create_interactive_shell(TestPmdShell, privileged=True)
> - dev_list = [str(x) for x in testpmd_driver.get_devices()]
> + with testpmd_driver as testpmd:
> + dev_list = [str(x) for x in testpmd.get_devices()]
> for nic in self.nics_in_node:
> self.verify(
> nic.pci in dev_list,
> --
> 2.44.0
>
next prev parent reply other threads:[~2024-05-22 13:53 UTC|newest]
Thread overview: 79+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-14 20:14 [PATCH v1 0/4] Add second scatter test case jspewock
2024-05-14 20:14 ` [PATCH v1 1/4] dts: improve starting and stopping interactive shells jspewock
2024-05-20 17:17 ` Luca Vizzarro
2024-05-22 13:43 ` Patrick Robb
2024-05-14 20:14 ` [PATCH v1 2/4] dts: add context manager for " jspewock
2024-05-20 17:30 ` Luca Vizzarro
2024-05-29 20:37 ` Jeremy Spewock
2024-05-22 13:53 ` Patrick Robb [this message]
2024-05-29 20:37 ` Jeremy Spewock
2024-05-14 20:14 ` [PATCH v1 3/4] dts: add methods for modifying MTU to testpmd shell jspewock
2024-05-20 17:35 ` Luca Vizzarro
2024-05-29 20:38 ` Jeremy Spewock
2024-05-22 16:10 ` Patrick Robb
2024-05-14 20:14 ` [PATCH v1 4/4] dts: add test case that utilizes offload to pmd_buffer_scatter jspewock
2024-05-20 17:56 ` Luca Vizzarro
2024-05-29 20:40 ` Jeremy Spewock
2024-05-30 9:47 ` Luca Vizzarro
2024-05-30 16:33 ` [PATCH v2 0/4] Add second scatter test case jspewock
2024-05-30 16:33 ` [PATCH v2 1/4] dts: improve starting and stopping interactive shells jspewock
2024-05-31 16:37 ` Luca Vizzarro
2024-05-31 21:07 ` Jeremy Spewock
2024-05-30 16:33 ` [PATCH v2 2/4] dts: add context manager for " jspewock
2024-05-31 16:38 ` Luca Vizzarro
2024-05-30 16:33 ` [PATCH v2 3/4] dts: add methods for modifying MTU to testpmd shell jspewock
2024-05-31 16:34 ` Luca Vizzarro
2024-05-31 21:08 ` Jeremy Spewock
2024-06-10 14:35 ` Juraj Linkeš
2024-05-30 16:33 ` [PATCH v2 4/4] dts: add test case that utilizes offload to pmd_buffer_scatter jspewock
2024-05-31 16:33 ` Luca Vizzarro
2024-05-31 21:08 ` Jeremy Spewock
2024-06-05 21:31 ` [PATCH v3 0/4] Add second scatter test case jspewock
2024-06-05 21:31 ` [PATCH v3 1/4] dts: improve starting and stopping interactive shells jspewock
2024-06-10 13:36 ` Juraj Linkeš
2024-06-10 19:27 ` Jeremy Spewock
2024-06-05 21:31 ` [PATCH v3 2/4] dts: add context manager for " jspewock
2024-06-10 14:31 ` Juraj Linkeš
2024-06-10 20:06 ` Jeremy Spewock
2024-06-11 9:17 ` Juraj Linkeš
2024-06-11 15:33 ` Jeremy Spewock
2024-06-12 8:37 ` Juraj Linkeš
2024-06-05 21:31 ` [PATCH v3 3/4] dts: add methods for modifying MTU to testpmd shell jspewock
2024-06-10 15:03 ` Juraj Linkeš
2024-06-10 20:07 ` Jeremy Spewock
2024-06-05 21:31 ` [PATCH v3 4/4] dts: add test case that utilizes offload to pmd_buffer_scatter jspewock
2024-06-10 15:22 ` Juraj Linkeš
2024-06-10 20:08 ` Jeremy Spewock
2024-06-11 9:22 ` Juraj Linkeš
2024-06-11 15:33 ` Jeremy Spewock
2024-06-13 18:15 ` [PATCH v4 0/4] Add second scatter test case jspewock
2024-06-13 18:15 ` [PATCH v4 1/4] dts: add context manager for interactive shells jspewock
2024-06-18 15:47 ` Juraj Linkeš
2024-06-13 18:15 ` [PATCH v4 2/4] dts: improve starting and stopping " jspewock
2024-06-18 15:54 ` Juraj Linkeš
2024-06-18 16:47 ` Jeremy Spewock
2024-06-13 18:15 ` [PATCH v4 3/4] dts: add methods for modifying MTU to testpmd shell jspewock
2024-06-19 8:16 ` Juraj Linkeš
2024-06-20 19:23 ` Jeremy Spewock
2024-06-21 8:08 ` Juraj Linkeš
2024-06-13 18:15 ` [PATCH v4 4/4] dts: add test case that utilizes offload to pmd_buffer_scatter jspewock
2024-06-19 8:51 ` Juraj Linkeš
2024-06-20 19:24 ` Jeremy Spewock
2024-06-21 8:32 ` Juraj Linkeš
2024-06-25 16:27 ` [PATCH v5 0/4] Add second scatter test case jspewock
2024-06-25 16:27 ` [PATCH v5 1/4] dts: add context manager for interactive shells jspewock
2024-06-25 16:27 ` [PATCH v5 2/4] dts: improve starting and stopping " jspewock
2024-06-25 16:27 ` [PATCH v5 3/4] dts: add methods for modifying MTU to testpmd shell jspewock
2024-06-25 16:27 ` [PATCH v5 4/4] dts: add test case that utilizes offload to pmd_buffer_scatter jspewock
2024-06-28 17:32 ` [PATCH v6 0/4] Add second scatter test case jspewock
2024-06-28 17:32 ` [PATCH v6 1/4] dts: add context manager for interactive shells jspewock
2024-06-28 17:32 ` [PATCH v6 2/4] dts: improve starting and stopping " jspewock
2024-06-28 17:32 ` [PATCH v6 3/4] dts: add methods for modifying MTU to testpmd shell jspewock
2024-06-28 17:32 ` [PATCH v6 4/4] dts: add test case that utilizes offload to pmd_buffer_scatter jspewock
2024-07-09 17:53 ` [PATCH v7 0/2] Add second scatter test case jspewock
2024-07-09 17:53 ` [PATCH v7 1/2] dts: add methods for modifying MTU to testpmd shell jspewock
2024-08-20 13:05 ` Juraj Linkeš
2024-08-20 14:38 ` Jeremy Spewock
2024-07-09 17:53 ` [PATCH v7 2/2] dts: add test case that utilizes offload to pmd_buffer_scatter jspewock
2024-08-27 17:22 ` [PATCH v8 0/1] dts: add second scatter test case jspewock
2024-08-27 17:22 ` [PATCH v8 1/1] dts: add test case that utilizes offload to pmd_buffer_scatter jspewock
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='CAJvnSUBsrP8ffsd=swJyPdM_089jm1WSc-VTDG=Zf=HmujpqFQ@mail.gmail.com' \
--to=probb@iol.unh.edu \
--cc=Honnappa.Nagarahalli@arm.com \
--cc=Luca.Vizzarro@arm.com \
--cc=dev@dpdk.org \
--cc=jspewock@iol.unh.edu \
--cc=juraj.linkes@pantheon.tech \
--cc=paul.szczepanek@arm.com \
--cc=thomas@monjalon.net \
--cc=wathsala.vithanage@arm.com \
--cc=yoan.picchi@foss.arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).