From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 88FCA43421; Fri, 1 Dec 2023 18:33:20 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 20AFE40298; Fri, 1 Dec 2023 18:33:20 +0100 (CET) Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) by mails.dpdk.org (Postfix) with ESMTP id 36A954025F for ; Fri, 1 Dec 2023 18:33:18 +0100 (CET) Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-1d03fb57b69so7045575ad.1 for ; Fri, 01 Dec 2023 09:33:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iol.unh.edu; s=unh-iol; t=1701451997; x=1702056797; darn=dpdk.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=QyLl5vL19p98Iv+ra56/upH3cPQuG/5LEnLBRyjd5iA=; b=N44Ze+TiHhjR09g+nhn3TN79F+xmTGQH/rYmW2QGysq5rP/3dbqt/xXg6kY4dLr4og p5+cOkWXfdJ3leBxwzyYFf5CS9JuXEB+2NiMYhfd+iO+un78+J+q8OGGDie5GjRsDcv5 dvr6IIS5fqkU/kh9vXq11eSErClcQUza9V1yM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701451997; x=1702056797; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=QyLl5vL19p98Iv+ra56/upH3cPQuG/5LEnLBRyjd5iA=; b=BzAUQjBwtykezz4O/8DidS88vIZwN7vwB74cKlOdoDDPNt+rpsqSGjqqaqwCB8V/x5 ewNd8JunkvJRNSufdT2sHocu8pisMTL20WzVaPc0ve+hBw8Se7rRnWZxJ5uL38L2vanv bsdaELdkCX+jkrglmETuWqcUMhABVxWKdBc2EOUcpxXJNxRBm5PYhd/xXenk6uSXBDNO azgF68YbUkTsREEPuhBoS14OBfuHoqtoui3qyR2yOQ8fwaHABj60j/wt1Ee+V5wx9wxH OdlJSsM6YMTKNLAEJ4TdDu0hJTl2myDLgtnRgg0Ld8uzp5r3kiay+LwZSe5+GXZZF/6p vdUw== X-Gm-Message-State: AOJu0Yxk7Cow7guzg5CUy/K6vmuowx0TXIh+3taaow7QwLsg/Y+m1vVM pK2xwRQYGkwW1pipa9zNOPVvI3ZVRYdteT2i+bwiMw== X-Google-Smtp-Source: AGHT+IGGTvxOrZSiAfARI71KyvXpuOGKMNfO82RFWGNhDT30+ZHiLDcgonAZPPgyhMhashoaFB3BeJzYJ1CGVhVlRcA= X-Received: by 2002:a17:90b:3b89:b0:285:d975:72e9 with SMTP id pc9-20020a17090b3b8900b00285d97572e9mr15613028pjb.22.1701451997138; Fri, 01 Dec 2023 09:33:17 -0800 (PST) MIME-Version: 1.0 References: <20231115130959.39420-1-juraj.linkes@pantheon.tech> <20231123151344.162812-1-juraj.linkes@pantheon.tech> <20231123151344.162812-16-juraj.linkes@pantheon.tech> In-Reply-To: <20231123151344.162812-16-juraj.linkes@pantheon.tech> From: Jeremy Spewock Date: Fri, 1 Dec 2023 12:33:06 -0500 Message-ID: Subject: Re: [PATCH v8 15/21] dts: os session docstring update To: =?UTF-8?Q?Juraj_Linke=C5=A1?= Cc: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, probb@iol.unh.edu, paul.szczepanek@arm.com, yoan.picchi@foss.arm.com, Luca.Vizzarro@arm.com, dev@dpdk.org Content-Type: multipart/alternative; boundary="000000000000e4698d060b7629ea" X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org --000000000000e4698d060b7629ea Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Thu, Nov 23, 2023 at 10:14=E2=80=AFAM Juraj Linke=C5=A1 wrote: > Format according to the Google format and PEP257, with slight > deviations. > > Signed-off-by: Juraj Linke=C5=A1 > --- > dts/framework/testbed_model/os_session.py | 272 ++++++++++++++++------ > 1 file changed, 205 insertions(+), 67 deletions(-) > > diff --git a/dts/framework/testbed_model/os_session.py > b/dts/framework/testbed_model/os_session.py > index 76e595a518..cfdbd1c4bd 100644 > --- a/dts/framework/testbed_model/os_session.py > +++ b/dts/framework/testbed_model/os_session.py > @@ -2,6 +2,26 @@ > # Copyright(c) 2023 PANTHEON.tech s.r.o. > # Copyright(c) 2023 University of New Hampshire > > +"""OS-aware remote session. > + > +DPDK supports multiple different operating systems, meaning it can run o= n > these different operating > +systems. This module defines the common API that OS-unaware layers use > and translates the API into > +OS-aware calls/utility usage. > + > +Note: > + Running commands with administrative privileges requires OS > awareness. This is the only layer > + that's aware of OS differences, so this is where non-privileged > command get converted > + to privileged commands. > + > +Example: > + A user wishes to remove a directory on a remote > :class:`~.sut_node.SutNode`. > + The :class:`~.sut_node.SutNode` object isn't aware what OS the node > is running - it delegates > + the OS translation logic to :attr:`~.node.Node.main_session`. The SU= T > node calls > + :meth:`~OSSession.remove_remote_dir` with a generic, OS-unaware path > and > + the :attr:`~.node.Node.main_session` translates that to ``rm -rf`` i= f > the node's OS is Linux > + and other commands for other OSs. It also translates the path to > match the underlying OS. > +""" > + > from abc import ABC, abstractmethod > from collections.abc import Iterable > from ipaddress import IPv4Interface, IPv6Interface > @@ -28,10 +48,16 @@ > > > class OSSession(ABC): > - """ > - The OS classes create a DTS node remote session and implement OS > specific > + """OS-unaware to OS-aware translation API definition. > + > + The OSSession classes create a remote session to a DTS node and > implement OS specific > behavior. There a few control methods implemented by the base class, > the rest need > - to be implemented by derived classes. > + to be implemented by subclasses. > + > + Attributes: > + name: The name of the session. > + remote_session: The remote session maintaining the connection to > the node. > + interactive_session: The interactive remote session maintaining > the connection to the node. > """ > > _config: NodeConfiguration > @@ -46,6 +72,15 @@ def __init__( > name: str, > logger: DTSLOG, > ): > + """Initialize the OS-aware session. > + > + Connect to the node right away and also create an interactive > remote session. > + > + Args: > + node_config: The test run configuration of the node to > connect to. > + name: The name of the session. > + logger: The logger instance this session will use. > + """ > self._config =3D node_config > self.name =3D name > self._logger =3D logger > @@ -53,15 +88,15 @@ def __init__( > self.interactive_session =3D > create_interactive_session(node_config, logger) > > def close(self, force: bool =3D False) -> None: > - """ > - Close the remote session. > + """Close the underlying remote session. > + > + Args: > + force: Force the closure of the connection. > """ > self.remote_session.close(force) > > def is_alive(self) -> bool: > - """ > - Check whether the remote session is still responding. > - """ > + """Check whether the underlying remote session is still > responding.""" > return self.remote_session.is_alive() > > def send_command( > @@ -72,10 +107,23 @@ def send_command( > verify: bool =3D False, > env: dict | None =3D None, > ) -> CommandResult: > - """ > - An all-purpose API in case the command to be executed is already > - OS-agnostic, such as when the path to the executed command has > been > - constructed beforehand. > + """An all-purpose API for OS-agnostic commands. > + > + This can be used for an execution of a portable command that's > executed the same way > + on all operating systems, such as Python. > + > + The :option:`--timeout` command line argument and the > :envvar:`DTS_TIMEOUT` > + environment variable configure the timeout of command execution. > + > + Args: > + command: The command to execute. > + timeout: Wait at most this long in seconds for `command` > execution to complete. > + privileged: Whether to run the command with administrative > privileges. > + verify: If :data:`True`, will check the exit code of the > command. > + env: A dictionary with environment variables to be used with > the command execution. > + > + Raises: > + RemoteCommandExecutionError: If verify is :data:`True` and > the command failed. > """ > if privileged: > command =3D self._get_privileged_command(command) > @@ -89,8 +137,20 @@ def create_interactive_shell( > privileged: bool, > app_args: str, > ) -> InteractiveShellType: > - """ > - See "create_interactive_shell" in SutNode > + """Factory for interactive session handlers. > + > + Instantiate `shell_cls` according to the remote OS specifics. > + > + Args: > + shell_cls: The class of the shell. > + timeout: Timeout for reading output from the SSH channel. If > you are > + reading from the buffer and don't receive any data withi= n > the timeout > + it will throw an error. > + privileged: Whether to run the shell with administrative > privileges. > + app_args: The arguments to be passed to the application. > + > + Returns: > + An instance of the desired interactive application shell. > """ > return shell_cls( > self.interactive_session.session, > @@ -114,27 +174,42 @@ def _get_privileged_command(command: str) -> str: > > @abstractmethod > def guess_dpdk_remote_dir(self, remote_dir: str | PurePath) -> > PurePath: > - """ > - Try to find DPDK remote dir in remote_dir. > + """Try to find DPDK directory in `remote_dir`. > + > + The directory is the one which is created after the extraction o= f > the tarball. The files > + are usually extracted into a directory starting with ``dpdk-``. > + > + Returns: > + The absolute path of the DPDK remote directory, empty path i= f > not found. > """ > > @abstractmethod > def get_remote_tmp_dir(self) -> PurePath: > - """ > - Get the path of the temporary directory of the remote OS. > + """Get the path of the temporary directory of the remote OS. > + > + Returns: > + The absolute path of the temporary directory. > """ > > @abstractmethod > def get_dpdk_build_env_vars(self, arch: Architecture) -> dict: > - """ > - Create extra environment variables needed for the target > architecture. Get > - information from the node if needed. > + """Create extra environment variables needed for the target > architecture. > + > + Different architectures may require different configuration, suc= h > as setting 32-bit CFLAGS. > + > + Returns: > + A dictionary with keys as environment variables. > """ > > @abstractmethod > def join_remote_path(self, *args: str | PurePath) -> PurePath: > - """ > - Join path parts using the path separator that fits the remote OS= . > + """Join path parts using the path separator that fits the remote > OS. > + > + Args: > + args: Any number of paths to join. > + > + Returns: > + The resulting joined path. > """ > > @abstractmethod > @@ -143,13 +218,13 @@ def copy_from( > source_file: str | PurePath, > destination_file: str | PurePath, > ) -> None: > - """Copy a file from the remote Node to the local filesystem. > + """Copy a file from the remote node to the local filesystem. > > - Copy source_file from the remote Node associated with this remot= e > - session to destination_file on the local filesystem. > + Copy `source_file` from the remote node associated with this > remote > + session to `destination_file` on the local filesystem. > > Args: > - source_file: the file on the remote Node. > + source_file: the file on the remote node. > destination_file: a file or directory path on the local > filesystem. > """ > > @@ -159,14 +234,14 @@ def copy_to( > source_file: str | PurePath, > destination_file: str | PurePath, > ) -> None: > - """Copy a file from local filesystem to the remote Node. > + """Copy a file from local filesystem to the remote node. > > - Copy source_file from local filesystem to destination_file > - on the remote Node associated with this remote session. > + Copy `source_file` from local filesystem to `destination_file` > + on the remote node associated with this remote session. > > Args: > source_file: the file on the local filesystem. > - destination_file: a file or directory path on the remote Nod= e. > + destination_file: a file or directory path on the remote nod= e. > """ > > @abstractmethod > @@ -176,8 +251,12 @@ def remove_remote_dir( > recursive: bool =3D True, > force: bool =3D True, > ) -> None: > - """ > - Remove remote directory, by default remove recursively and > forcefully. > + """Remove remote directory, by default remove recursively and > forcefully. > + > + Args: > + remote_dir_path: The path of the directory to remove. > + recursive: If :data:`True`, also remove all contents inside > the directory. > + force: If :data:`True`, ignore all warnings and try to remov= e > at all costs. > """ > > @abstractmethod > @@ -186,9 +265,12 @@ def extract_remote_tarball( > remote_tarball_path: str | PurePath, > expected_dir: str | PurePath | None =3D None, > ) -> None: > - """ > - Extract remote tarball in place. If expected_dir is a non-empty > string, check > - whether the dir exists after extracting the archive. > + """Extract remote tarball in its remote directory. > + > + Args: > + remote_tarball_path: The path of the tarball on the remote > node. > + expected_dir: If non-empty, check whether `expected_dir` > exists after extracting > + the archive. > """ > > @abstractmethod > @@ -201,69 +283,119 @@ def build_dpdk( > rebuild: bool =3D False, > timeout: float =3D SETTINGS.compile_timeout, > ) -> None: > - """ > - Build DPDK in the input dir with specified environment variables > and meson > - arguments. > + """Build DPDK on the remote node. > + > + An extracted DPDK tarball must be present on the node. The build > consists of two steps:: > + > + meson setup remote_dpdk_dir remote_dpdk_build_d= ir > + ninja -C remote_dpdk_build_dir > + > + The :option:`--compile-timeout` command line argument and the > :envvar:`DTS_COMPILE_TIMEOUT` > + environment variable configure the timeout of DPDK build. > + > + Args: > + env_vars: Use these environment variables then building DPDK= . > I think this is meant to be "when building DPDK" instead. > + meson_args: Use these meson arguments when building DPDK. > + remote_dpdk_dir: The directory on the remote node where DPDK > will be built. > + remote_dpdk_build_dir: The target build directory on the > remote node. > + rebuild: If :data:`True`, do a subsequent build with ``meson > configure`` instead > + of ``meson setup``. > + timeout: Wait at most this long in seconds for the build > execution to complete. > """ > > @abstractmethod > def get_dpdk_version(self, version_path: str | PurePath) -> str: > - """ > - Inspect DPDK version on the remote node from version_path. > + """Inspect the DPDK version on the remote node. > + > + Args: > + version_path: The path to the VERSION file containing the > DPDK version. > + > + Returns: > + The DPDK version. > """ > > @abstractmethod > def get_remote_cpus(self, use_first_core: bool) -> list[LogicalCore]= : > - """ > - Compose a list of LogicalCores present on the remote node. > - If use_first_core is False, the first physical core won't be use= d. > + r"""Get the list of :class:`~.cpu.LogicalCore`\s on the remote > node. > + > + Args: > + use_first_core: If :data:`False`, the first physical core > won't be used. > + > + Returns: > + The logical cores present on the node. > """ > > @abstractmethod > def kill_cleanup_dpdk_apps(self, dpdk_prefix_list: Iterable[str]) -> > None: > - """ > - Kill and cleanup all DPDK apps identified by dpdk_prefix_list. I= f > - dpdk_prefix_list is empty, attempt to find running DPDK apps to > kill and clean. > + """Kill and cleanup all DPDK apps. > + > + Args: > + dpdk_prefix_list: Kill all apps identified by > `dpdk_prefix_list`. > + If `dpdk_prefix_list` is empty, attempt to find running > DPDK apps to kill and clean. > """ > > @abstractmethod > def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str: > - """ > - Get the DPDK file prefix that will be used when running DPDK app= s. > + """Make OS-specific modification to the DPDK file prefix. > + > + Args: > + dpdk_prefix: The OS-unaware file prefix. > + > + Returns: > + The OS-specific file prefix. > """ > > @abstractmethod > - def setup_hugepages(self, hugepage_amount: int, force_first_numa: > bool) -> None: > - """ > - Get the node's Hugepage Size, configure the specified amount of > hugepages > + def setup_hugepages(self, hugepage_count: int, force_first_numa: > bool) -> None: > + """Configure hugepages on the node. > + > + Get the node's Hugepage Size, configure the specified count of > hugepages > if needed and mount the hugepages if needed. > - If force_first_numa is True, configure hugepages just on the > first socket. > + > + Args: > + hugepage_count: Configure this many hugepages. > + force_first_numa: If :data:`True`, configure hugepages just > on the first numa node. > """ > > @abstractmethod > def get_compiler_version(self, compiler_name: str) -> str: > - """ > - Get installed version of compiler used for DPDK > + """Get installed version of compiler used for DPDK. > + > + Args: > + compiler_name: The name of the compiler executable. > + > + Returns: > + The compiler's version. > """ > > @abstractmethod > def get_node_info(self) -> NodeInfo: > - """ > - Collect information about the node > + """Collect additional information about the node. > + > + Returns: > + Node information. > """ > > @abstractmethod > def update_ports(self, ports: list[Port]) -> None: > - """ > - Get additional information about ports: > - Logical name (e.g. enp7s0) if applicable > - Mac address > + """Get additional information about ports from the operating > system and update them. > + > + The additional information is: > + > + * Logical name (e.g. ``enp7s0``) if applicable, > + * Mac address. > + > + Args: > + ports: The ports to update. > """ > > @abstractmethod > def configure_port_state(self, port: Port, enable: bool) -> None: > - """ > - Enable/disable port. > + """Enable/disable `port` in the operating system. > + > + Args: > + port: The port to configure. > + enable: If :data:`True`, enable the port, otherwise shut it > down. > """ > > @abstractmethod > @@ -273,12 +405,18 @@ def configure_port_ip_address( > port: Port, > delete: bool, > ) -> None: > - """ > - Configure (add or delete) an IP address of the input port. > + """Configure an IP address on `port` in the operating system. > + > + Args: > + address: The address to configure. > + port: The port to configure. > + delete: If :data:`True`, remove the IP address, otherwise > configure it. > """ > > @abstractmethod > def configure_ipv4_forwarding(self, enable: bool) -> None: > - """ > - Enable IPv4 forwarding in the underlying OS. > + """Enable IPv4 forwarding in the operating system. > + > + Args: > + enable: If :data:`True`, enable the forwarding, otherwise > disable it. > """ > -- > 2.34.1 > > --000000000000e4698d060b7629ea Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


<= div dir=3D"ltr" class=3D"gmail_attr">On Thu, Nov 23, 2023 at 10:14=E2=80=AF= AM Juraj Linke=C5=A1 <juraj.linkes@pantheon.tech> wrote:
Format according to the Goog= le format and PEP257, with slight
deviations.

Signed-off-by: Juraj Linke=C5=A1 <juraj.linkes@pantheon.tech>
---
=C2=A0dts/framework/testbed_model/os_session.py | 272 ++++++++++++++++-----= -
=C2=A01 file changed, 205 insertions(+), 67 deletions(-)

diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/test= bed_model/os_session.py
index 76e595a518..cfdbd1c4bd 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -2,6 +2,26 @@
=C2=A0# Copyright(c) 2023 PANTHEON.tech s.r.o.
=C2=A0# Copyright(c) 2023 University of New Hampshire

+"""OS-aware remote session.
+
+DPDK supports multiple different operating systems, meaning it can run on = these different operating
+systems. This module defines the common API that OS-unaware layers use and= translates the API into
+OS-aware calls/utility usage.
+
+Note:
+=C2=A0 =C2=A0 Running commands with administrative privileges requires OS = awareness. This is the only layer
+=C2=A0 =C2=A0 that's aware of OS differences, so this is where non-pri= vileged command get converted
+=C2=A0 =C2=A0 to privileged commands.
+
+Example:
+=C2=A0 =C2=A0 A user wishes to remove a directory on a remote :class:`~.su= t_node.SutNode`.
+=C2=A0 =C2=A0 The :class:`~.sut_node.SutNode` object isn't aware what = OS the node is running - it delegates
+=C2=A0 =C2=A0 the OS translation logic to :attr:`~.node.Node.main_session`= . The SUT node calls
+=C2=A0 =C2=A0 :meth:`~OSSession.remove_remote_dir` with a generic, OS-unaw= are path and
+=C2=A0 =C2=A0 the :attr:`~.node.Node.main_session` translates that to ``rm= -rf`` if the node's OS is Linux
+=C2=A0 =C2=A0 and other commands for other OSs. It also translates the pat= h to match the underlying OS.
+"""
+
=C2=A0from abc import ABC, abstractmethod
=C2=A0from collections.abc import Iterable
=C2=A0from ipaddress import IPv4Interface, IPv6Interface
@@ -28,10 +48,16 @@


=C2=A0class OSSession(ABC):
-=C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 The OS classes create a DTS node remote session and implemen= t OS specific
+=C2=A0 =C2=A0 """OS-unaware to OS-aware translation API def= inition.
+
+=C2=A0 =C2=A0 The OSSession classes create a remote session to a DTS node = and implement OS specific
=C2=A0 =C2=A0 =C2=A0behavior. There a few control methods implemented by th= e base class, the rest need
-=C2=A0 =C2=A0 to be implemented by derived classes.
+=C2=A0 =C2=A0 to be implemented by subclasses.
+
+=C2=A0 =C2=A0 Attributes:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 name: The name of the session.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 remote_session: The remote session maintaining= the connection to the node.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 interactive_session: The interactive remote se= ssion maintaining the connection to the node.
=C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0_config: NodeConfiguration
@@ -46,6 +72,15 @@ def __init__(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0name: str,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0logger: DTSLOG,
=C2=A0 =C2=A0 =C2=A0):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Initialize the OS-aware sess= ion.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Connect to the node right away and also create= an interactive remote session.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 node_config: The test run config= uration of the node to connect to.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name: The name of the session. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 logger: The logger instance this= session will use.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self._config =3D node_config
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self.name =3D name
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self._logger =3D logger
@@ -53,15 +88,15 @@ def __init__(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self.interactive_session =3D create_inter= active_session(node_config, logger)

=C2=A0 =C2=A0 =C2=A0def close(self, force: bool =3D False) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Close the remote session.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Close the underlying remote = session.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 force: Force the closure of the = connection.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self.remote_session.close(force)

=C2=A0 =C2=A0 =C2=A0def is_alive(self) -> bool:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Check whether the remote session is still resp= onding.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Check whether the underlying= remote session is still responding."""
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return self.remote_session.is_alive()

=C2=A0 =C2=A0 =C2=A0def send_command(
@@ -72,10 +107,23 @@ def send_command(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0verify: bool =3D False,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0env: dict | None =3D None,
=C2=A0 =C2=A0 =C2=A0) -> CommandResult:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 An all-purpose API in case the command to be e= xecuted is already
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 OS-agnostic, such as when the path to the exec= uted command has been
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 constructed beforehand.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """An all-purpose API for OS-ag= nostic commands.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 This can be used for an execution of a portabl= e command that's executed the same way
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 on all operating systems, such as Python.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The :option:`--timeout` command line argument = and the :envvar:`DTS_TIMEOUT`
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 environment variable configure the timeout of = command execution.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 command: The command to execute.=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 timeout: Wait at most this long = in seconds for `command` execution to complete.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 privileged: Whether to run the c= ommand with administrative privileges.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 verify: If :data:`True`, will ch= eck the exit code of the command.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 env: A dictionary with environme= nt variables to be used with the command execution.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Raises:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RemoteCommandExecutionError: If = verify is :data:`True` and the command failed.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if privileged:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0command =3D self._get_privi= leged_command(command)
@@ -89,8 +137,20 @@ def create_interactive_shell(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0privileged: bool,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0app_args: str,
=C2=A0 =C2=A0 =C2=A0) -> InteractiveShellType:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 See "create_interactive_shell" in Su= tNode
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Factory for interactive sess= ion handlers.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Instantiate `shell_cls` according to the remot= e OS specifics.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 shell_cls: The class of the shel= l.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 timeout: Timeout for reading out= put from the SSH channel. If you are
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 reading from the b= uffer and don't receive any data within the timeout
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 it will throw an e= rror.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 privileged: Whether to run the s= hell with administrative privileges.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 app_args: The arguments to be pa= ssed to the application.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 An instance of the desired inter= active application shell.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return shell_cls(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0self.interactive_session.se= ssion,
@@ -114,27 +174,42 @@ def _get_privileged_command(command: str) -> str:<= br>
=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def guess_dpdk_remote_dir(self, remote_dir: str | PureP= ath) -> PurePath:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Try to find DPDK remote dir in remote_dir.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Try to find DPDK directory i= n `remote_dir`.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The directory is the one which is created afte= r the extraction of the tarball. The files
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 are usually extracted into a directory startin= g with ``dpdk-``.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The absolute path of the DPDK re= mote directory, empty path if not found.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def get_remote_tmp_dir(self) -> PurePath:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Get the path of the temporary directory of the= remote OS.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get the path of the temporar= y directory of the remote OS.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The absolute path of the tempora= ry directory.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def get_dpdk_build_env_vars(self, arch: Architecture) -= > dict:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Create extra environment variables needed for = the target architecture. Get
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 information from the node if needed.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Create extra environment var= iables needed for the target architecture.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Different architectures may require different = configuration, such as setting 32-bit CFLAGS.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 A dictionary with keys as enviro= nment variables.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def join_remote_path(self, *args: str | PurePath) ->= PurePath:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Join path parts using the path separator that = fits the remote OS.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Join path parts using the pa= th separator that fits the remote OS.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 args: Any number of paths to joi= n.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The resulting joined path.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
@@ -143,13 +218,13 @@ def copy_from(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0source_file: str | PurePath,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0destination_file: str | PurePath,
=C2=A0 =C2=A0 =C2=A0) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Copy a file from the remote = Node to the local filesystem.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Copy a file from the remote = node to the local filesystem.

-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Copy source_file from the remote Node associat= ed with this remote
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 session to destination_file on the local files= ystem.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Copy `source_file` from the remote node associ= ated with this remote
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 session to `destination_file` on the local fil= esystem.

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Args:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 source_file: the file on the rem= ote Node.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 source_file: the file on the rem= ote node.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0destination_file: a file or= directory path on the local filesystem.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

@@ -159,14 +234,14 @@ def copy_to(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0source_file: str | PurePath,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0destination_file: str | PurePath,
=C2=A0 =C2=A0 =C2=A0) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Copy a file from local files= ystem to the remote Node.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Copy a file from local files= ystem to the remote node.

-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Copy source_file from local filesystem to dest= ination_file
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 on the remote Node associated with this remote= session.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Copy `source_file` from local filesystem to `d= estination_file`
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 on the remote node associated with this remote= session.

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Args:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0source_file: the file on th= e local filesystem.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 destination_file: a file or dire= ctory path on the remote Node.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 destination_file: a file or dire= ctory path on the remote node.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
@@ -176,8 +251,12 @@ def remove_remote_dir(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0recursive: bool =3D True,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0force: bool =3D True,
=C2=A0 =C2=A0 =C2=A0) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Remove remote directory, by default remove rec= ursively and forcefully.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Remove remote directory, by = default remove recursively and forcefully.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 remote_dir_path: The path of the= directory to remove.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 recursive: If :data:`True`, also= remove all contents inside the directory.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 force: If :data:`True`, ignore a= ll warnings and try to remove at all costs.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
@@ -186,9 +265,12 @@ def extract_remote_tarball(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0remote_tarball_path: str | PurePath,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0expected_dir: str | PurePath | None =3D N= one,
=C2=A0 =C2=A0 =C2=A0) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Extract remote tarball in place. If expected_d= ir is a non-empty string, check
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 whether the dir exists after extracting the ar= chive.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Extract remote tarball in it= s remote directory.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 remote_tarball_path: The path of= the tarball on the remote node.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 expected_dir: If non-empty, chec= k whether `expected_dir` exists after extracting
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 the archive.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
@@ -201,69 +283,119 @@ def build_dpdk(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rebuild: bool =3D False,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0timeout: float =3D SETTINGS.compile_timeo= ut,
=C2=A0 =C2=A0 =C2=A0) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Build DPDK in the input dir with specified env= ironment variables and meson
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 arguments.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Build DPDK on the remote nod= e.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 An extracted DPDK tarball must be present on t= he node. The build consists of two steps::
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 meson setup <meson args> r= emote_dpdk_dir remote_dpdk_build_dir
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ninja -C remote_dpdk_build_dir +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The :option:`--compile-timeout` command line a= rgument and the :envvar:`DTS_COMPILE_TIMEOUT`
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 environment variable configure the timeout of = DPDK build.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 env_vars: Use these environment = variables then building DPDK.

I think this is = meant to be "when building DPDK" instead.
=C2=A0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 meson_args: Use these meson argu= ments when building DPDK.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 remote_dpdk_dir: The directory o= n the remote node where DPDK will be built.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 remote_dpdk_build_dir: The targe= t build directory on the remote node.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rebuild: If :data:`True`, do a s= ubsequent build with ``meson configure`` instead
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 of ``meson setup``= .
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 timeout: Wait at most this long = in seconds for the build execution to complete.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def get_dpdk_version(self, version_path: str | PurePath= ) -> str:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Inspect DPDK version on the remote node from v= ersion_path.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Inspect the DPDK version on = the remote node.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 version_path: The path to the VE= RSION file containing the DPDK version.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The DPDK version.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def get_remote_cpus(self, use_first_core: bool) -> l= ist[LogicalCore]:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Compose a list of LogicalCores present on the = remote node.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 If use_first_core is False, the first physical= core won't be used.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 r"""Get the list of :class:`~.c= pu.LogicalCore`\s on the remote node.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 use_first_core: If :data:`False`= , the first physical core won't be used.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The logical cores present on the= node.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def kill_cleanup_dpdk_apps(self, dpdk_prefix_list: Iter= able[str]) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Kill and cleanup all DPDK apps identified by d= pdk_prefix_list. If
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 dpdk_prefix_list is empty, attempt to find run= ning DPDK apps to kill and clean.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Kill and cleanup all DPDK ap= ps.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dpdk_prefix_list: Kill all apps = identified by `dpdk_prefix_list`.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 If `dpdk_prefix_li= st` is empty, attempt to find running DPDK apps to kill and clean.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def get_dpdk_file_prefix(self, dpdk_prefix: str) -> = str:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Get the DPDK file prefix that will be used whe= n running DPDK apps.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Make OS-specific modificatio= n to the DPDK file prefix.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dpdk_prefix: The OS-unaware file = prefix.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The OS-specific file prefix.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
-=C2=A0 =C2=A0 def setup_hugepages(self, hugepage_amount: int, force_first_= numa: bool) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Get the node's Hugepage Size, configure th= e specified amount of hugepages
+=C2=A0 =C2=A0 def setup_hugepages(self, hugepage_count: int, force_first_n= uma: bool) -> None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Configure hugepages on the n= ode.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Get the node's Hugepage Size, configure th= e specified count of hugepages
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if needed and mount the hugepages if need= ed.
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 If force_first_numa is True, configure hugepag= es just on the first socket.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hugepage_count: Configure this m= any hugepages.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 force_first_numa:=C2=A0 If :data= :`True`, configure hugepages just on the first numa node.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def get_compiler_version(self, compiler_name: str) ->= ; str:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Get installed version of compiler used for DPD= K
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get installed version of com= piler used for DPDK.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 compiler_name: The name of the c= ompiler executable.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 The compiler's version.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def get_node_info(self) -> NodeInfo:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Collect information about the node
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Collect additional informati= on about the node.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Returns:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Node information.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def update_ports(self, ports: list[Port]) -> None: -=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Get additional information about ports:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Logical name (e.g. enp7s0) if ap= plicable
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Mac address
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Get additional information a= bout ports from the operating system and update them.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 The additional information is:
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * Logical name (e.g. ``enp7s0``)= if applicable,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * Mac address.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ports: The ports to update.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def configure_port_state(self, port: Port, enable: bool= ) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Enable/disable port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Enable/disable `port` in the= operating system.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port to configure.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: If :data:`True`, enable = the port, otherwise shut it down.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
@@ -273,12 +405,18 @@ def configure_port_ip_address(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0port: Port,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0delete: bool,
=C2=A0 =C2=A0 =C2=A0) -> None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Configure (add or delete) an IP address of the= input port.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Configure an IP address on `= port` in the operating system.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 address: The address to configur= e.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port: The port to configure.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 delete: If :data:`True`, remove = the IP address, otherwise configure it.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""

=C2=A0 =C2=A0 =C2=A0@abstractmethod
=C2=A0 =C2=A0 =C2=A0def configure_ipv4_forwarding(self, enable: bool) ->= None:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 """
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 Enable IPv4 forwarding in the underlying OS. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Enable IPv4 forwarding in th= e operating system.
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 Args:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable: If :data:`True`, enable = the forwarding, otherwise disable it.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"""
--
2.34.1

--000000000000e4698d060b7629ea--