Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/134584/?format=api
http://patches.dpdk.org/api/patches/134584/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/patch/20231123151344.162812-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": "<20231123151344.162812-19-juraj.linkes@pantheon.tech>", "list_archive_url": "https://inbox.dpdk.org/dev/20231123151344.162812-19-juraj.linkes@pantheon.tech", "date": "2023-11-23T15:13:41", "name": "[v8,18/21] dts: sut and tg nodes docstring update", "commit_ref": null, "pull_url": null, "state": "superseded", "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/20231123151344.162812-19-juraj.linkes@pantheon.tech/mbox/", "series": [ { "id": 30375, "url": "http://patches.dpdk.org/api/series/30375/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=30375", "date": "2023-11-23T15:13:23", "name": "dts: docstrings update", "version": 8, "mbox": "http://patches.dpdk.org/series/30375/mbox/" } ], "comments": "http://patches.dpdk.org/api/patches/134584/comments/", "check": "success", "checks": "http://patches.dpdk.org/api/patches/134584/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 EE078433AC;\n\tThu, 23 Nov 2023 16:16:42 +0100 (CET)", "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 30A74432B8;\n\tThu, 23 Nov 2023 16:14:28 +0100 (CET)", "from mail-wm1-f45.google.com (mail-wm1-f45.google.com\n [209.85.128.45]) by mails.dpdk.org (Postfix) with ESMTP id DF55E43251\n for <dev@dpdk.org>; Thu, 23 Nov 2023 16:14:10 +0100 (CET)", "by mail-wm1-f45.google.com with SMTP id\n 5b1f17b1804b1-40b2a8575d9so6405525e9.0\n for <dev@dpdk.org>; Thu, 23 Nov 2023 07:14:10 -0800 (PST)", "from jlinkes-PT-Latitude-5530.. ([84.245.121.10])\n by smtp.gmail.com with ESMTPSA id\n q4-20020adfea04000000b003296b488961sm1870143wrm.31.2023.11.23.07.14.09\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 23 Nov 2023 07:14:09 -0800 (PST)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=pantheon.tech; s=google; t=1700752450; x=1701357250; 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=JpQ/yfTD0d7/3tkZ6DpH+t9O08mbRCARRnOC7qvl9kuKN+ewfUaDMSTjFWpxnP5uw7\n jUnaJhIXBnfYfaU9CraKO9CAIgkgxeZ/LewGT5XfBGX6XRMid/bWxTBMURiQvGpmAYBa\n VFd9YfFm7WmY2R2ypgWxV6eEAkIakxGsmYsTy68z+SOWF+e/HVP+B6MPGo0pGofnLT+g\n iRpTJU9Mi5dIgdTedFAAB4G5GKLH2ScwKy9Jzyf0SfYjS+KwNl89U5OaQihxSC4apNkC\n SJ6NRYgfxjq22bjcJTpyN0v6LDY5pt2WOfGcRPnBzabfY+o5WiLljnPEBLraWCplii6r\n q5lQ==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1700752450; x=1701357250;\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=YHBXNqnQIwSVpG2NCT9HwJA+tvYL6M4IyzyvXZCwRlD9aJ9Ujil+OBiMERB1Ij0yE0\n ubNa2SlexKIWxVL8WFZ8iyS+efs7DGzexZQrCrJgTAzZugI7BoLuYwx0wyyuI6Ynpb3l\n ac+bIhKURk+6X5ui6zV+gYpo75NMVA4f9WRyB0R2o6tmJpXh8kLj6/TJlNgVyJIDKm4Y\n 2utkDkAFEeIDz9MHCvfgMou8AnU312MCYMNGv9ri2KCr3qUVrIuH+uF8ENPiUdMomcBB\n tT3e9kX7g1jIheXvCoGSMFJC3fgnnzbwNbUaUGKNaH80avDbfqkQxA1xMjuKRNm5revW\n iEbg==", "X-Gm-Message-State": "AOJu0YxzpOLGQ/RfpEgLwRK94N/kwkv7foAtJHb1oXzfRSh1BFeZxsuQ\n Jw/4HZ0GW7oAMY/ub96Nkmvkpw==", "X-Google-Smtp-Source": "\n AGHT+IH2Co3MWV61PSRXaex3I85fvks+fFZ1r1PkTqmMNglsxn8KVRt0/K4NDZ9IkWMHseziHQSbbQ==", "X-Received": "by 2002:adf:cd86:0:b0:332:e573:5c63 with SMTP id\n q6-20020adfcd86000000b00332e5735c63mr902178wrj.7.1700752450380;\n Thu, 23 Nov 2023 07:14:10 -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 v8 18/21] dts: sut and tg nodes docstring update", "Date": "Thu, 23 Nov 2023 16:13:41 +0100", "Message-Id": "<20231123151344.162812-19-juraj.linkes@pantheon.tech>", "X-Mailer": "git-send-email 2.34.1", "In-Reply-To": "<20231123151344.162812-1-juraj.linkes@pantheon.tech>", "References": "<20231115130959.39420-1-juraj.linkes@pantheon.tech>\n <20231123151344.162812-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": [ "v8", "18/21" ] }{ "id": 134584, "url": "