get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 138828,
    "url": "http://patches.dpdk.org/api/patches/138828/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20240326190422.577028-3-luca.vizzarro@arm.com/",
    "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": "<20240326190422.577028-3-luca.vizzarro@arm.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20240326190422.577028-3-luca.vizzarro@arm.com",
    "date": "2024-03-26T19:04:18",
    "name": "[2/6] dts: use Params for interactive shells",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "c013ddf7158af74e088430dea37f50031f8adf37",
    "submitter": {
        "id": 3197,
        "url": "http://patches.dpdk.org/api/people/3197/?format=api",
        "name": "Luca Vizzarro",
        "email": "luca.vizzarro@arm.com"
    },
    "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/20240326190422.577028-3-luca.vizzarro@arm.com/mbox/",
    "series": [
        {
            "id": 31622,
            "url": "http://patches.dpdk.org/api/series/31622/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=31622",
            "date": "2024-03-26T19:04:16",
            "name": "dts: add testpmd params and statefulness",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/31622/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/138828/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/138828/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 034C043D5B;\n\tTue, 26 Mar 2024 20:04:48 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 0EAB0410E6;\n\tTue, 26 Mar 2024 20:04:36 +0100 (CET)",
            "from foss.arm.com (foss.arm.com [217.140.110.172])\n by mails.dpdk.org (Postfix) with ESMTP id 46028410D3\n for <dev@dpdk.org>; Tue, 26 Mar 2024 20:04:34 +0100 (CET)",
            "from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14])\n by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6E5952F4;\n Tue, 26 Mar 2024 12:05:07 -0700 (PDT)",
            "from localhost.localdomain (unknown [10.57.16.115])\n by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id AB0643F64C;\n Tue, 26 Mar 2024 12:04:32 -0700 (PDT)"
        ],
        "From": "Luca Vizzarro <luca.vizzarro@arm.com>",
        "To": "dev@dpdk.org",
        "Cc": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>,\n Luca Vizzarro <luca.vizzarro@arm.com>,\n Jack Bond-Preston <jack.bond-preston@arm.com>,\n Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>",
        "Subject": "[PATCH 2/6] dts: use Params for interactive shells",
        "Date": "Tue, 26 Mar 2024 19:04:18 +0000",
        "Message-Id": "<20240326190422.577028-3-luca.vizzarro@arm.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20240326190422.577028-1-luca.vizzarro@arm.com>",
        "References": "<20240326190422.577028-1-luca.vizzarro@arm.com>",
        "MIME-Version": "1.0",
        "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": "Make it so that interactive shells accept an implementation of `Params`\nfor app arguments. Convert EalParameters to use `Params` instead.\n\nString command line parameters can still be supplied by using the\n`StrParams` implementation.\n\nSigned-off-by: Luca Vizzarro <luca.vizzarro@arm.com>\nReviewed-by: Jack Bond-Preston <jack.bond-preston@arm.com>\nReviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>\n---\n .../remote_session/interactive_shell.py       |   8 +-\n dts/framework/remote_session/testpmd_shell.py |  12 +-\n dts/framework/testbed_model/__init__.py       |   2 +-\n dts/framework/testbed_model/node.py           |   4 +-\n dts/framework/testbed_model/os_session.py     |   4 +-\n dts/framework/testbed_model/sut_node.py       | 106 ++++++++----------\n dts/tests/TestSuite_pmd_buffer_scatter.py     |   3 +-\n 7 files changed, 73 insertions(+), 66 deletions(-)",
    "diff": "diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py\nindex 5cfe202e15..a2c7b30d9f 100644\n--- a/dts/framework/remote_session/interactive_shell.py\n+++ b/dts/framework/remote_session/interactive_shell.py\n@@ -1,5 +1,6 @@\n # SPDX-License-Identifier: BSD-3-Clause\n # Copyright(c) 2023 University of New Hampshire\n+# Copyright(c) 2024 Arm Limited\n \n \"\"\"Common functionality for interactive shell handling.\n \n@@ -21,6 +22,7 @@\n from paramiko import Channel, SSHClient, channel  # type: ignore[import]\n \n from framework.logger import DTSLogger\n+from framework.params import Params\n from framework.settings import SETTINGS\n \n \n@@ -40,7 +42,7 @@ class InteractiveShell(ABC):\n     _ssh_channel: Channel\n     _logger: DTSLogger\n     _timeout: float\n-    _app_args: str\n+    _app_args: Params | None\n \n     #: Prompt to expect at the end of output when sending a command.\n     #: This is often overridden by subclasses.\n@@ -63,7 +65,7 @@ def __init__(\n         interactive_session: SSHClient,\n         logger: DTSLogger,\n         get_privileged_command: Callable[[str], str] | None,\n-        app_args: str = \"\",\n+        app_args: Params | None = None,\n         timeout: float = SETTINGS.timeout,\n     ) -> None:\n         \"\"\"Create an SSH channel during initialization.\n@@ -100,7 +102,7 @@ def _start_application(self, get_privileged_command: Callable[[str], str] | None\n             get_privileged_command: A function (but could be any callable) that produces\n                 the version of the command with elevated privileges.\n         \"\"\"\n-        start_command = f\"{self.path} {self._app_args}\"\n+        start_command = f\"{self.path} {self._app_args or ''}\"\n         if get_privileged_command is not None:\n             start_command = get_privileged_command(start_command)\n         self.send_command(start_command)\ndiff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py\nindex cb2ab6bd00..db3abb7600 100644\n--- a/dts/framework/remote_session/testpmd_shell.py\n+++ b/dts/framework/remote_session/testpmd_shell.py\n@@ -21,6 +21,7 @@\n from typing import Callable, ClassVar\n \n from framework.exception import InteractiveCommandExecutionError\n+from framework.params import StrParams\n from framework.settings import SETTINGS\n from framework.utils import StrEnum\n \n@@ -118,8 +119,15 @@ def _start_application(self, get_privileged_command: Callable[[str], str] | None\n         Also find the number of pci addresses which were allowed on the command line when the app\n         was started.\n         \"\"\"\n-        self._app_args += \" -i --mask-event intr_lsc\"\n-        self.number_of_ports = self._app_args.count(\"-a \")\n+        from framework.testbed_model.sut_node import EalParameters\n+\n+        assert isinstance(self._app_args, EalParameters)\n+\n+        if isinstance(self._app_args.app_params, StrParams):\n+            self._app_args.app_params.value += \" -i --mask-event intr_lsc\"\n+\n+        self.number_of_ports = len(self._app_args.ports) if self._app_args.ports is not None else 0\n+\n         super()._start_application(get_privileged_command)\n \n     def start(self, verify: bool = True) -> None:\ndiff --git a/dts/framework/testbed_model/__init__.py b/dts/framework/testbed_model/__init__.py\nindex 6086512ca2..ef9520df4c 100644\n--- a/dts/framework/testbed_model/__init__.py\n+++ b/dts/framework/testbed_model/__init__.py\n@@ -23,6 +23,6 @@\n from .cpu import LogicalCoreCount, LogicalCoreCountFilter, LogicalCoreList\n from .node import Node\n from .port import Port, PortLink\n-from .sut_node import SutNode\n+from .sut_node import SutNode, EalParameters\n from .tg_node import TGNode\n from .virtual_device import VirtualDevice\ndiff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py\nindex 74061f6262..ec9512d618 100644\n--- a/dts/framework/testbed_model/node.py\n+++ b/dts/framework/testbed_model/node.py\n@@ -2,6 +2,7 @@\n # Copyright(c) 2010-2014 Intel Corporation\n # Copyright(c) 2022-2023 PANTHEON.tech s.r.o.\n # Copyright(c) 2022-2023 University of New Hampshire\n+# Copyright(c) 2024 Arm Limited\n \n \"\"\"Common functionality for node management.\n \n@@ -24,6 +25,7 @@\n )\n from framework.exception import ConfigurationError\n from framework.logger import DTSLogger, get_dts_logger\n+from framework.params import Params\n from framework.settings import SETTINGS\n \n from .cpu import (\n@@ -199,7 +201,7 @@ def create_interactive_shell(\n         shell_cls: Type[InteractiveShellType],\n         timeout: float = SETTINGS.timeout,\n         privileged: bool = False,\n-        app_args: str = \"\",\n+        app_args: Params | None = None,\n     ) -> InteractiveShellType:\n         \"\"\"Factory for interactive session handlers.\n \ndiff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py\nindex d5bf7e0401..7234c975c8 100644\n--- a/dts/framework/testbed_model/os_session.py\n+++ b/dts/framework/testbed_model/os_session.py\n@@ -1,6 +1,7 @@\n # SPDX-License-Identifier: BSD-3-Clause\n # Copyright(c) 2023 PANTHEON.tech s.r.o.\n # Copyright(c) 2023 University of New Hampshire\n+# Copyright(c) 2024 Arm Limited\n \n \"\"\"OS-aware remote session.\n \n@@ -29,6 +30,7 @@\n \n from framework.config import Architecture, NodeConfiguration, NodeInfo\n from framework.logger import DTSLogger\n+from framework.params import Params\n from framework.remote_session import (\n     CommandResult,\n     InteractiveRemoteSession,\n@@ -134,7 +136,7 @@ def create_interactive_shell(\n         shell_cls: Type[InteractiveShellType],\n         timeout: float,\n         privileged: bool,\n-        app_args: str,\n+        app_args: Params | None,\n     ) -> InteractiveShellType:\n         \"\"\"Factory for interactive session handlers.\n \ndiff --git a/dts/framework/testbed_model/sut_node.py b/dts/framework/testbed_model/sut_node.py\nindex 97aa26d419..3f8c3807b3 100644\n--- a/dts/framework/testbed_model/sut_node.py\n+++ b/dts/framework/testbed_model/sut_node.py\n@@ -2,6 +2,7 @@\n # Copyright(c) 2010-2014 Intel Corporation\n # Copyright(c) 2023 PANTHEON.tech s.r.o.\n # Copyright(c) 2023 University of New Hampshire\n+# Copyright(c) 2024 Arm Limited\n \n \"\"\"System under test (DPDK + hardware) node.\n \n@@ -11,6 +12,7 @@\n \"\"\"\n \n \n+from dataclasses import dataclass, field\n import os\n import tarfile\n import time\n@@ -23,6 +25,8 @@\n     NodeInfo,\n     SutNodeConfiguration,\n )\n+from framework import params\n+from framework.params import Params, StrParams\n from framework.remote_session import CommandResult\n from framework.settings import SETTINGS\n from framework.utils import MesonArgs\n@@ -34,62 +38,51 @@\n from .virtual_device import VirtualDevice\n \n \n-class EalParameters(object):\n+def _port_to_pci(port: Port) -> str:\n+    return port.pci\n+\n+\n+@dataclass(kw_only=True)\n+class EalParameters(Params):\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-        memory_channels: int,\n-        prefix: str,\n-        no_pci: bool,\n-        vdevs: list[VirtualDevice],\n-        ports: list[Port],\n-        other_eal_param: str,\n-    ):\n-        \"\"\"Initialize the parameters according to inputs.\n-\n-        Process the parameters into the format used on the command line.\n+    lcore_list: LogicalCoreList = field(metadata=params.short(\"l\"))\n+    \"\"\"The list of logical cores to use.\"\"\"\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+    memory_channels: int = field(metadata=params.short(\"n\"))\n+    \"\"\"The number of memory channels to use.\"\"\"\n \n-                vdevs=[\n-                    VirtualDevice('net_ring0'),\n-                    VirtualDevice('net_ring1')\n-                ]\n-            ports: The list of ports to allow.\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-        self._prefix = prefix\n-        if prefix:\n-            self._prefix = f\"--file-prefix={prefix}\"\n-        self._no_pci = \"--no-pci\" if no_pci else \"\"\n-        self._vdevs = \" \".join(f\"--vdev {vdev}\" for vdev in vdevs)\n-        self._ports = \" \".join(f\"-a {port.pci}\" for port in ports)\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-            f\"{self._prefix} \"\n-            f\"{self._no_pci} \"\n-            f\"{self._vdevs} \"\n-            f\"{self._ports} \"\n-            f\"{self._other_eal_param}\"\n-        )\n+    prefix: str = field(metadata=params.long(\"file-prefix\"))\n+    \"\"\"Set the file prefix string with which to start DPDK, e.g.: ``prefix=\"vf\"``.\"\"\"\n+\n+    no_pci: params.Option\n+    \"\"\"Switch to disable PCI bus e.g.: ``no_pci=True``.\"\"\"\n+\n+    vdevs: list[VirtualDevice] | None = field(\n+        default=None, metadata=params.multiple(params.long(\"vdev\"))\n+    )\n+    \"\"\"Virtual devices, e.g.::\n+\n+        vdevs=[\n+            VirtualDevice(\"net_ring0\"),\n+            VirtualDevice(\"net_ring1\")\n+        ]\n+    \"\"\"\n+\n+    ports: list[Port] | None = field(\n+        default=None,\n+        metadata=params.field_mixins(_port_to_pci, metadata=params.multiple(params.short(\"a\"))),\n+    )\n+    \"\"\"The list of ports to allow.\"\"\"\n+\n+    other_eal_param: StrParams | None = None\n+    \"\"\"Any other EAL parameter(s).\"\"\"\n+\n+    app_params: Params | None = field(default=None, metadata=params.options_end())\n+    \"\"\"Parameters to pass to the underlying DPDK app.\"\"\"\n \n \n class SutNode(Node):\n@@ -350,7 +343,7 @@ def create_eal_parameters(\n         ascending_cores: bool = True,\n         prefix: str = \"dpdk\",\n         append_prefix_timestamp: bool = True,\n-        no_pci: bool = False,\n+        no_pci: params.Option = None,\n         vdevs: list[VirtualDevice] | None = None,\n         ports: list[Port] | None = None,\n         other_eal_param: str = \"\",\n@@ -393,9 +386,6 @@ def create_eal_parameters(\n         if prefix:\n             self._dpdk_prefix_list.append(prefix)\n \n-        if vdevs is None:\n-            vdevs = []\n-\n         if ports is None:\n             ports = self.ports\n \n@@ -406,7 +396,7 @@ def create_eal_parameters(\n             no_pci=no_pci,\n             vdevs=vdevs,\n             ports=ports,\n-            other_eal_param=other_eal_param,\n+            other_eal_param=StrParams(other_eal_param),\n         )\n \n     def run_dpdk_app(\n@@ -442,7 +432,7 @@ def create_interactive_shell(\n         shell_cls: Type[InteractiveShellType],\n         timeout: float = SETTINGS.timeout,\n         privileged: bool = False,\n-        app_parameters: str = \"\",\n+        app_parameters: Params | None = None,\n         eal_parameters: EalParameters | None = None,\n     ) -> InteractiveShellType:\n         \"\"\"Extend the factory for interactive session handlers.\n@@ -459,6 +449,7 @@ def create_interactive_shell(\n                 reading from the buffer and don't receive any data within the timeout\n                 it will throw an error.\n             privileged: Whether to run the shell with administrative privileges.\n+            app_args: The arguments to be passed to the application.\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                 :meth:`create_eal_parameters`.\n@@ -470,9 +461,10 @@ def create_interactive_shell(\n         \"\"\"\n         # We need to append the build directory and add EAL parameters for DPDK apps\n         if shell_cls.dpdk_app:\n-            if not eal_parameters:\n+            if eal_parameters is None:\n                 eal_parameters = self.create_eal_parameters()\n-            app_parameters = f\"{eal_parameters} -- {app_parameters}\"\n+            eal_parameters.app_params = app_parameters\n+            app_parameters = eal_parameters\n \n             shell_cls.path = self.main_session.join_remote_path(\n                 self.remote_dpdk_build_dir, shell_cls.path\ndiff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py\nindex 3701c47408..4cdbdc4272 100644\n--- a/dts/tests/TestSuite_pmd_buffer_scatter.py\n+++ b/dts/tests/TestSuite_pmd_buffer_scatter.py\n@@ -22,6 +22,7 @@\n from scapy.packet import Raw  # type: ignore[import]\n from scapy.utils import hexstr  # type: ignore[import]\n \n+from framework.params import StrParams\n from framework.remote_session.testpmd_shell import TestPmdForwardingModes, TestPmdShell\n from framework.test_suite import TestSuite\n \n@@ -103,7 +104,7 @@ def pmd_scatter(self, mbsize: int) -> None:\n         \"\"\"\n         testpmd = self.sut_node.create_interactive_shell(\n             TestPmdShell,\n-            app_parameters=(\n+            app_parameters=StrParams(\n                 \"--mbcache=200 \"\n                 f\"--mbuf-size={mbsize} \"\n                 \"--max-pkt-len=9000 \"\n",
    "prefixes": [
        "2/6"
    ]
}