get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/134806/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 134806,
    "url": "http://patches.dpdk.org/api/patches/134806/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20231204102429.106709-19-juraj.linkes@pantheon.tech/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20231204102429.106709-19-juraj.linkes@pantheon.tech>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20231204102429.106709-19-juraj.linkes@pantheon.tech",
    "date": "2023-12-04T10:24:26",
    "name": "[v9,18/21] dts: sut and tg nodes docstring update",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "61329667efd51d4deda3bf350042ff7958661083",
    "submitter": {
        "id": 1626,
        "url": "http://patches.dpdk.org/api/people/1626/?format=api",
        "name": "Juraj Linkeš",
        "email": "juraj.linkes@pantheon.tech"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20231204102429.106709-19-juraj.linkes@pantheon.tech/mbox/",
    "series": [
        {
            "id": 30441,
            "url": "http://patches.dpdk.org/api/series/30441/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=30441",
            "date": "2023-12-04T10:24:08",
            "name": "dts: docstrings update",
            "version": 9,
            "mbox": "http://patches.dpdk.org/series/30441/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/134806/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/134806/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 34D244366A;\n\tMon,  4 Dec 2023 11:26:46 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 6510842D89;\n\tMon,  4 Dec 2023 11:24:57 +0100 (CET)",
            "from mail-wm1-f51.google.com (mail-wm1-f51.google.com\n [209.85.128.51]) by mails.dpdk.org (Postfix) with ESMTP id B4690427E3\n for <dev@dpdk.org>; Mon,  4 Dec 2023 11:24:49 +0100 (CET)",
            "by mail-wm1-f51.google.com with SMTP id\n 5b1f17b1804b1-40c09fcfa9fso10724285e9.2\n for <dev@dpdk.org>; Mon, 04 Dec 2023 02:24:49 -0800 (PST)",
            "from jlinkes-PT-Latitude-5530.pantheon.local ([81.89.53.154])\n by smtp.gmail.com with ESMTPSA id\n m28-20020a05600c3b1c00b0040b2b38a1fasm14255415wms.4.2023.12.04.02.24.48\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 04 Dec 2023 02:24:48 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=pantheon.tech; s=google; t=1701685489; x=1702290289; darn=dpdk.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=1GQUWl4fKCWCJwAOefP/gX8Mj3lF66GL8g1NajdfKkM=;\n b=ubcdVFPeGbpTDM2kwLATSXQzyNOGediHcOic8kohHrPSxpY2kEBDxxvUyITSXcU/Xf\n SsxX3P8W4x4c/VZuUekr2TUToTcLKCnQXliey2Ps8MhWHssTME2+I5iajlk/0Nnei51y\n QB9x+pWjIB7kUH8niipI61cU+0A/lVIOpJhMhjIPilIQB1Z+SnxCbrojY0z+FOCW0R1T\n o0SLiz/KLefiD0McGqnyZmncJhoNHT3VLmQW4PR/nMUEtlON4KCNDNdA94J8xpDA6grN\n fCn1mlPZTeRqj0hlOW/7hilvoQat4yWn0zEU6ebfHdFnJTagBQaDcCWEetYW2iuoz4UQ\n 3z+w==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1701685489; x=1702290289;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n :subject:date:message-id:reply-to;\n bh=1GQUWl4fKCWCJwAOefP/gX8Mj3lF66GL8g1NajdfKkM=;\n b=F7XePAHHyHF0CufRKRkW/OFbBoyfWPuD90PcluR5OSi5+NL7krym0wRoAFD2O5FMQp\n un/QGaOyh3jVGmh/EfmgSJT28tV1GVpN+frSMrYSAh7GVQOhD/eth1wOn8BNTB9m9Iih\n 5RaY5u35dOGHycuxJ2GpRwKkmfqLcqf22Bs2Yu4zZJy6l5JFybkSrS3A40O1T7B6eigX\n qhMvDfeWX1ZxnA432CSaqrtcDW8T6GVO77VEX+zUeh48W+2UhSHQ52c3c4VXSRVDdJN6\n IWV2fmGRo2QCeLk5id5yZ6Bx4gNP5LG6kwY0o5SlOf8prz2CdQcoWE9pq78KpYGxPPa7\n WaQQ==",
        "X-Gm-Message-State": "AOJu0Ywqp0mwXsB0cwGX+K3ptsFvMxY3cirPxxeYKdfldzXfN76Tww1E\n SP7qfbHyXJSvu+uvs9fEbQxImg==",
        "X-Google-Smtp-Source": "\n AGHT+IGlQbKp9Ya8pztd+ja2JyNnfcVFsxET/dMyscxRfYeoqXWqK7QbEobGQxbwWWkVTR3rG8bTzg==",
        "X-Received": "by 2002:a7b:c8c4:0:b0:40b:5f03:b405 with SMTP id\n f4-20020a7bc8c4000000b0040b5f03b405mr1097481wml.295.1701685489282;\n Mon, 04 Dec 2023 02:24:49 -0800 (PST)",
        "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "To": "thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu,\n probb@iol.unh.edu, paul.szczepanek@arm.com, yoan.picchi@foss.arm.com,\n Luca.Vizzarro@arm.com",
        "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "Subject": "[PATCH v9 18/21] dts: sut and tg nodes docstring update",
        "Date": "Mon,  4 Dec 2023 11:24:26 +0100",
        "Message-Id": "<20231204102429.106709-19-juraj.linkes@pantheon.tech>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20231204102429.106709-1-juraj.linkes@pantheon.tech>",
        "References": "<20231123151344.162812-1-juraj.linkes@pantheon.tech>\n <20231204102429.106709-1-juraj.linkes@pantheon.tech>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "Format according to the Google format and PEP257, with slight\ndeviations.\n\nSigned-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>\n---\n dts/framework/testbed_model/sut_node.py | 230 ++++++++++++++++--------\n dts/framework/testbed_model/tg_node.py  |  42 +++--\n 2 files changed, 176 insertions(+), 96 deletions(-)",
    "diff": "diff --git a/dts/framework/testbed_model/sut_node.py b/dts/framework/testbed_model/sut_node.py\nindex 5ce9446dba..c4acea38d1 100644\n--- a/dts/framework/testbed_model/sut_node.py\n+++ b/dts/framework/testbed_model/sut_node.py\n@@ -3,6 +3,14 @@\n # Copyright(c) 2023 PANTHEON.tech s.r.o.\n # Copyright(c) 2023 University of New Hampshire\n \n+\"\"\"System under test (DPDK + hardware) node.\n+\n+A system under test (SUT) is the combination of DPDK\n+and the hardware we're testing with DPDK (NICs, crypto and other devices).\n+An SUT node is where this SUT runs.\n+\"\"\"\n+\n+\n import os\n import tarfile\n import time\n@@ -26,6 +34,11 @@\n \n \n class EalParameters(object):\n+    \"\"\"The environment abstraction layer parameters.\n+\n+    The string representation can be created by converting the instance to a string.\n+    \"\"\"\n+\n     def __init__(\n         self,\n         lcore_list: LogicalCoreList,\n@@ -35,21 +48,23 @@ def __init__(\n         vdevs: list[VirtualDevice],\n         other_eal_param: str,\n     ):\n-        \"\"\"\n-        Generate eal parameters character string;\n-        :param lcore_list: the list of logical cores to use.\n-        :param memory_channels: the number of memory channels to use.\n-        :param prefix: set file prefix string, eg:\n-                        prefix='vf'\n-        :param no_pci: switch of disable PCI bus eg:\n-                        no_pci=True\n-        :param vdevs: virtual device list, eg:\n-                        vdevs=[\n-                            VirtualDevice('net_ring0'),\n-                            VirtualDevice('net_ring1')\n-                        ]\n-        :param other_eal_param: user defined DPDK eal parameters, eg:\n-                        other_eal_param='--single-file-segments'\n+        \"\"\"Initialize the parameters according to inputs.\n+\n+        Process the parameters into the format used on the command line.\n+\n+        Args:\n+            lcore_list: The list of logical cores to use.\n+            memory_channels: The number of memory channels to use.\n+            prefix: Set the file prefix string with which to start DPDK, e.g.: ``prefix='vf'``.\n+            no_pci: Switch to disable PCI bus e.g.: ``no_pci=True``.\n+            vdevs: Virtual devices, e.g.::\n+\n+                vdevs=[\n+                    VirtualDevice('net_ring0'),\n+                    VirtualDevice('net_ring1')\n+                ]\n+            other_eal_param: user defined DPDK EAL parameters, e.g.:\n+                ``other_eal_param='--single-file-segments'``\n         \"\"\"\n         self._lcore_list = f\"-l {lcore_list}\"\n         self._memory_channels = f\"-n {memory_channels}\"\n@@ -61,6 +76,7 @@ def __init__(\n         self._other_eal_param = other_eal_param\n \n     def __str__(self) -> str:\n+        \"\"\"Create the EAL string.\"\"\"\n         return (\n             f\"{self._lcore_list} \"\n             f\"{self._memory_channels} \"\n@@ -72,11 +88,21 @@ def __str__(self) -> str:\n \n \n class SutNode(Node):\n-    \"\"\"\n-    A class for managing connections to the System under Test, providing\n-    methods that retrieve the necessary information about the node (such as\n-    CPU, memory and NIC details) and configuration capabilities.\n-    Another key capability is building DPDK according to given build target.\n+    \"\"\"The system under test node.\n+\n+    The SUT node extends :class:`Node` with DPDK specific features:\n+\n+        * DPDK build,\n+        * Gathering of DPDK build info,\n+        * The running of DPDK apps, interactively or one-time execution,\n+        * DPDK apps cleanup.\n+\n+    The :option:`--tarball` command line argument and the :envvar:`DTS_DPDK_TARBALL`\n+    environment variable configure the path to the DPDK tarball\n+    or the git commit ID, tag ID or tree ID to test.\n+\n+    Attributes:\n+        config: The SUT node configuration\n     \"\"\"\n \n     config: SutNodeConfiguration\n@@ -94,6 +120,11 @@ class SutNode(Node):\n     _path_to_devbind_script: PurePath | None\n \n     def __init__(self, node_config: SutNodeConfiguration):\n+        \"\"\"Extend the constructor with SUT node specifics.\n+\n+        Args:\n+            node_config: The SUT node's test run configuration.\n+        \"\"\"\n         super(SutNode, self).__init__(node_config)\n         self._dpdk_prefix_list = []\n         self._build_target_config = None\n@@ -113,6 +144,12 @@ def __init__(self, node_config: SutNodeConfiguration):\n \n     @property\n     def _remote_dpdk_dir(self) -> PurePath:\n+        \"\"\"The remote DPDK dir.\n+\n+        This internal property should be set after extracting the DPDK tarball. If it's not set,\n+        that implies the DPDK setup step has been skipped, in which case we can guess where\n+        a previous build was located.\n+        \"\"\"\n         if self.__remote_dpdk_dir is None:\n             self.__remote_dpdk_dir = self._guess_dpdk_remote_dir()\n         return self.__remote_dpdk_dir\n@@ -123,6 +160,11 @@ def _remote_dpdk_dir(self, value: PurePath) -> None:\n \n     @property\n     def remote_dpdk_build_dir(self) -> PurePath:\n+        \"\"\"The remote DPDK build directory.\n+\n+        This is the directory where DPDK was built.\n+        We assume it was built in a subdirectory of the extracted tarball.\n+        \"\"\"\n         if self._build_target_config:\n             return self.main_session.join_remote_path(\n                 self._remote_dpdk_dir, self._build_target_config.name\n@@ -132,18 +174,21 @@ def remote_dpdk_build_dir(self) -> PurePath:\n \n     @property\n     def dpdk_version(self) -> str:\n+        \"\"\"Last built DPDK version.\"\"\"\n         if self._dpdk_version is None:\n             self._dpdk_version = self.main_session.get_dpdk_version(self._remote_dpdk_dir)\n         return self._dpdk_version\n \n     @property\n     def node_info(self) -> NodeInfo:\n+        \"\"\"Additional node information.\"\"\"\n         if self._node_info is None:\n             self._node_info = self.main_session.get_node_info()\n         return self._node_info\n \n     @property\n     def compiler_version(self) -> str:\n+        \"\"\"The node's compiler version.\"\"\"\n         if self._compiler_version is None:\n             if self._build_target_config is not None:\n                 self._compiler_version = self.main_session.get_compiler_version(\n@@ -158,6 +203,7 @@ def compiler_version(self) -> str:\n \n     @property\n     def path_to_devbind_script(self) -> PurePath:\n+        \"\"\"The path to the dpdk-devbind.py script on the node.\"\"\"\n         if self._path_to_devbind_script is None:\n             self._path_to_devbind_script = self.main_session.join_remote_path(\n                 self._remote_dpdk_dir, \"usertools\", \"dpdk-devbind.py\"\n@@ -165,6 +211,11 @@ def path_to_devbind_script(self) -> PurePath:\n         return self._path_to_devbind_script\n \n     def get_build_target_info(self) -> BuildTargetInfo:\n+        \"\"\"Get additional build target information.\n+\n+        Returns:\n+            The build target information,\n+        \"\"\"\n         return BuildTargetInfo(\n             dpdk_version=self.dpdk_version, compiler_version=self.compiler_version\n         )\n@@ -173,8 +224,9 @@ def _guess_dpdk_remote_dir(self) -> PurePath:\n         return self.main_session.guess_dpdk_remote_dir(self._remote_tmp_dir)\n \n     def _set_up_build_target(self, build_target_config: BuildTargetConfiguration) -> None:\n-        \"\"\"\n-        Setup DPDK on the SUT node.\n+        \"\"\"Setup DPDK on the SUT node.\n+\n+        Additional build target setup steps on top of those in :class:`Node`.\n         \"\"\"\n         # we want to ensure that dpdk_version and compiler_version is reset for new\n         # build targets\n@@ -186,16 +238,14 @@ def _set_up_build_target(self, build_target_config: BuildTargetConfiguration) ->\n         self.bind_ports_to_driver()\n \n     def _tear_down_build_target(self) -> None:\n-        \"\"\"\n-        This method exists to be optionally overwritten by derived classes and\n-        is not decorated so that the derived class doesn't have to use the decorator.\n+        \"\"\"Bind ports to the operating system drivers.\n+\n+        Additional build target teardown steps on top of those in :class:`Node`.\n         \"\"\"\n         self.bind_ports_to_driver(for_dpdk=False)\n \n     def _configure_build_target(self, build_target_config: BuildTargetConfiguration) -> None:\n-        \"\"\"\n-        Populate common environment variables and set build target config.\n-        \"\"\"\n+        \"\"\"Populate common environment variables and set build target config.\"\"\"\n         self._env_vars = {}\n         self._build_target_config = build_target_config\n         self._env_vars.update(self.main_session.get_dpdk_build_env_vars(build_target_config.arch))\n@@ -207,9 +257,7 @@ def _configure_build_target(self, build_target_config: BuildTargetConfiguration)\n \n     @Node.skip_setup\n     def _copy_dpdk_tarball(self) -> None:\n-        \"\"\"\n-        Copy to and extract DPDK tarball on the SUT node.\n-        \"\"\"\n+        \"\"\"Copy to and extract DPDK tarball on the SUT node.\"\"\"\n         self._logger.info(\"Copying DPDK tarball to SUT.\")\n         self.main_session.copy_to(SETTINGS.dpdk_tarball_path, self._remote_tmp_dir)\n \n@@ -238,8 +286,9 @@ def _copy_dpdk_tarball(self) -> None:\n \n     @Node.skip_setup\n     def _build_dpdk(self) -> None:\n-        \"\"\"\n-        Build DPDK. Uses the already configured target. Assumes that the tarball has\n+        \"\"\"Build DPDK.\n+\n+        Uses the already configured target. Assumes that the tarball has\n         already been copied to and extracted on the SUT node.\n         \"\"\"\n         self.main_session.build_dpdk(\n@@ -250,15 +299,19 @@ def _build_dpdk(self) -> None:\n         )\n \n     def build_dpdk_app(self, app_name: str, **meson_dpdk_args: str | bool) -> PurePath:\n-        \"\"\"\n-        Build one or all DPDK apps. Requires DPDK to be already built on the SUT node.\n-        When app_name is 'all', build all example apps.\n-        When app_name is any other string, tries to build that example app.\n-        Return the directory path of the built app. If building all apps, return\n-        the path to the examples directory (where all apps reside).\n-        The meson_dpdk_args are keyword arguments\n-        found in meson_option.txt in root DPDK directory. Do not use -D with them,\n-        for example: enable_kmods=True.\n+        \"\"\"Build one or all DPDK apps.\n+\n+        Requires DPDK to be already built on the SUT node.\n+\n+        Args:\n+            app_name: The name of the DPDK app to build.\n+                When `app_name` is ``all``, build all example apps.\n+            meson_dpdk_args: The arguments found in ``meson_options.txt`` in root DPDK directory.\n+                Do not use ``-D`` with them.\n+\n+        Returns:\n+            The directory path of the built app. If building all apps, return\n+            the path to the examples directory (where all apps reside).\n         \"\"\"\n         self.main_session.build_dpdk(\n             self._env_vars,\n@@ -277,9 +330,7 @@ def build_dpdk_app(self, app_name: str, **meson_dpdk_args: str | bool) -> PurePa\n         )\n \n     def kill_cleanup_dpdk_apps(self) -> None:\n-        \"\"\"\n-        Kill all dpdk applications on the SUT. Cleanup hugepages.\n-        \"\"\"\n+        \"\"\"Kill all dpdk applications on the SUT, then clean up hugepages.\"\"\"\n         if self._dpdk_kill_session and self._dpdk_kill_session.is_alive():\n             # we can use the session if it exists and responds\n             self._dpdk_kill_session.kill_cleanup_dpdk_apps(self._dpdk_prefix_list)\n@@ -298,33 +349,34 @@ def create_eal_parameters(\n         vdevs: list[VirtualDevice] | None = None,\n         other_eal_param: str = \"\",\n     ) -> \"EalParameters\":\n-        \"\"\"\n-        Generate eal parameters character string;\n-        :param lcore_filter_specifier: a number of lcores/cores/sockets to use\n-                        or a list of lcore ids to use.\n-                        The default will select one lcore for each of two cores\n-                        on one socket, in ascending order of core ids.\n-        :param ascending_cores: True, use cores with the lowest numerical id first\n-                        and continue in ascending order. If False, start with the\n-                        highest id and continue in descending order. This ordering\n-                        affects which sockets to consider first as well.\n-        :param prefix: set file prefix string, eg:\n-                        prefix='vf'\n-        :param append_prefix_timestamp: if True, will append a timestamp to\n-                        DPDK file prefix.\n-        :param no_pci: switch of disable PCI bus eg:\n-                        no_pci=True\n-        :param vdevs: virtual device list, eg:\n-                        vdevs=[\n-                            VirtualDevice('net_ring0'),\n-                            VirtualDevice('net_ring1')\n-                        ]\n-        :param other_eal_param: user defined DPDK eal parameters, eg:\n-                        other_eal_param='--single-file-segments'\n-        :return: eal param string, eg:\n-                '-c 0xf -a 0000:88:00.0 --file-prefix=dpdk_1112_20190809143420';\n-        \"\"\"\n+        \"\"\"Compose the EAL parameters.\n+\n+        Process the list of cores and the DPDK prefix and pass that along with\n+        the rest of the arguments.\n \n+        Args:\n+            lcore_filter_specifier: A number of lcores/cores/sockets to use\n+                or a list of lcore ids to use.\n+                The default will select one lcore for each of two cores\n+                on one socket, in ascending order of core ids.\n+            ascending_cores: Sort cores in ascending order (lowest to highest IDs).\n+                If :data:`False`, sort in descending order.\n+            prefix: Set the file prefix string with which to start DPDK, e.g.: ``prefix='vf'``.\n+            append_prefix_timestamp: If :data:`True`, will append a timestamp to DPDK file prefix.\n+            no_pci: Switch to disable PCI bus e.g.: ``no_pci=True``.\n+            vdevs: Virtual devices, e.g.::\n+\n+                vdevs=[\n+                    VirtualDevice('net_ring0'),\n+                    VirtualDevice('net_ring1')\n+                ]\n+            other_eal_param: user defined DPDK EAL parameters, e.g.:\n+                ``other_eal_param='--single-file-segments'``.\n+\n+        Returns:\n+            An EAL param string, such as\n+            ``-c 0xf -a 0000:88:00.0 --file-prefix=dpdk_1112_20190809143420``.\n+        \"\"\"\n         lcore_list = LogicalCoreList(self.filter_lcores(lcore_filter_specifier, ascending_cores))\n \n         if append_prefix_timestamp:\n@@ -348,14 +400,29 @@ def create_eal_parameters(\n     def run_dpdk_app(\n         self, app_path: PurePath, eal_args: \"EalParameters\", timeout: float = 30\n     ) -> CommandResult:\n-        \"\"\"\n-        Run DPDK application on the remote node.\n+        \"\"\"Run DPDK application on the remote node.\n+\n+        The application is not run interactively - the command that starts the application\n+        is executed and then the call waits for it to finish execution.\n+\n+        Args:\n+            app_path: The remote path to the DPDK application.\n+            eal_args: EAL parameters to run the DPDK application with.\n+            timeout: Wait at most this long in seconds for `command` execution to complete.\n+\n+        Returns:\n+            The result of the DPDK app execution.\n         \"\"\"\n         return self.main_session.send_command(\n             f\"{app_path} {eal_args}\", timeout, privileged=True, verify=True\n         )\n \n     def configure_ipv4_forwarding(self, enable: bool) -> None:\n+        \"\"\"Enable/disable IPv4 forwarding on the node.\n+\n+        Args:\n+            enable: If :data:`True`, enable the forwarding, otherwise disable it.\n+        \"\"\"\n         self.main_session.configure_ipv4_forwarding(enable)\n \n     def create_interactive_shell(\n@@ -365,9 +432,13 @@ def create_interactive_shell(\n         privileged: bool = False,\n         eal_parameters: EalParameters | str | None = None,\n     ) -> InteractiveShellType:\n-        \"\"\"Factory method for creating a handler for an interactive session.\n+        \"\"\"Extend the factory for interactive session handlers.\n+\n+        The extensions are SUT node specific:\n \n-        Instantiate shell_cls according to the remote OS specifics.\n+            * The default for `eal_parameters`,\n+            * The interactive shell path `shell_cls.path` is prepended with path to the remote\n+              DPDK build directory for DPDK apps.\n \n         Args:\n             shell_cls: The class of the shell.\n@@ -377,9 +448,10 @@ def create_interactive_shell(\n             privileged: Whether to run the shell with administrative privileges.\n             eal_parameters: List of EAL parameters to use to launch the app. If this\n                 isn't provided or an empty string is passed, it will default to calling\n-                create_eal_parameters().\n+                :meth:`create_eal_parameters`.\n+\n         Returns:\n-            Instance of the desired interactive application.\n+            An instance of the desired interactive application shell.\n         \"\"\"\n         if not eal_parameters:\n             eal_parameters = self.create_eal_parameters()\n@@ -396,8 +468,8 @@ def bind_ports_to_driver(self, for_dpdk: bool = True) -> None:\n         \"\"\"Bind all ports on the SUT to a driver.\n \n         Args:\n-            for_dpdk: Boolean that, when True, binds ports to os_driver_for_dpdk\n-            or, when False, binds to os_driver. Defaults to True.\n+            for_dpdk: If :data:`True`, binds ports to os_driver_for_dpdk.\n+                If :data:`False`, binds to os_driver.\n         \"\"\"\n         for port in self.ports:\n             driver = port.os_driver_for_dpdk if for_dpdk else port.os_driver\ndiff --git a/dts/framework/testbed_model/tg_node.py b/dts/framework/testbed_model/tg_node.py\nindex 8a8f0019f3..f269d4c585 100644\n--- a/dts/framework/testbed_model/tg_node.py\n+++ b/dts/framework/testbed_model/tg_node.py\n@@ -5,13 +5,8 @@\n \n \"\"\"Traffic generator node.\n \n-This is the node where the traffic generator resides.\n-The distinction between a node and a traffic generator is as follows:\n-A node is a host that DTS connects to. It could be a baremetal server,\n-a VM or a container.\n-A traffic generator is software running on the node.\n-A traffic generator node is a node running a traffic generator.\n-A node can be a traffic generator node as well as system under test node.\n+A traffic generator (TG) generates traffic that's sent towards the SUT node.\n+A TG node is where the TG runs.\n \"\"\"\n \n from scapy.packet import Packet  # type: ignore[import]\n@@ -24,13 +19,16 @@\n \n \n class TGNode(Node):\n-    \"\"\"Manage connections to a node with a traffic generator.\n+    \"\"\"The traffic generator node.\n \n-    Apart from basic node management capabilities, the Traffic Generator node has\n-    specialized methods for handling the traffic generator running on it.\n+    The TG node extends :class:`Node` with TG specific features:\n \n-    Arguments:\n-        node_config: The user configuration of the traffic generator node.\n+        * Traffic generator initialization,\n+        * The sending of traffic and receiving packets,\n+        * The sending of traffic without receiving packets.\n+\n+    Not all traffic generators are capable of capturing traffic, which is why there\n+    must be a way to send traffic without that.\n \n     Attributes:\n         traffic_generator: The traffic generator running on the node.\n@@ -39,6 +37,13 @@ class TGNode(Node):\n     traffic_generator: CapturingTrafficGenerator\n \n     def __init__(self, node_config: TGNodeConfiguration):\n+        \"\"\"Extend the constructor with TG node specifics.\n+\n+        Initialize the traffic generator on the TG node.\n+\n+        Args:\n+            node_config: The TG node's test run configuration.\n+        \"\"\"\n         super(TGNode, self).__init__(node_config)\n         self.traffic_generator = create_traffic_generator(self, node_config.traffic_generator)\n         self._logger.info(f\"Created node: {self.name}\")\n@@ -50,17 +55,17 @@ def send_packet_and_capture(\n         receive_port: Port,\n         duration: float = 1,\n     ) -> list[Packet]:\n-        \"\"\"Send a packet, return received traffic.\n+        \"\"\"Send `packet`, return received traffic.\n \n-        Send a packet on the send_port and then return all traffic captured\n-        on the receive_port for the given duration. Also record the captured traffic\n+        Send `packet` on `send_port` and then return all traffic captured\n+        on `receive_port` for the given duration. Also record the captured traffic\n         in a pcap file.\n \n         Args:\n             packet: The packet to send.\n             send_port: The egress port on the TG node.\n             receive_port: The ingress port in the TG node.\n-            duration: Capture traffic for this amount of time after sending the packet.\n+            duration: Capture traffic for this amount of time after sending `packet`.\n \n         Returns:\n              A list of received packets. May be empty if no packets are captured.\n@@ -70,6 +75,9 @@ def send_packet_and_capture(\n         )\n \n     def close(self) -> None:\n-        \"\"\"Free all resources used by the node\"\"\"\n+        \"\"\"Free all resources used by the node.\n+\n+        This extends the superclass method with TG cleanup.\n+        \"\"\"\n         self.traffic_generator.close()\n         super(TGNode, self).close()\n",
    "prefixes": [
        "v9",
        "18/21"
    ]
}