* [PATCH v1 0/4] dts: add dynamic queue configuration test suite
@ 2024-06-25 15:53 jspewock
2024-06-25 15:53 ` [PATCH v1 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
` (7 more replies)
0 siblings, 8 replies; 81+ messages in thread
From: jspewock @ 2024-06-25 15:53 UTC (permalink / raw)
To: Honnappa.Nagarahalli, wathsala.vithanage, npratte, thomas,
juraj.linkes, probb, Luca.Vizzarro, paul.szczepanek, yoan.picchi
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch ports over a test suite from old DTS that tests the
implementation of runtime port queue configuration of PMDs. It also
includes necessary framework changes to allow this to happen such as:
* Sending multiple packets and not capturing the result
* Only adjusting addresses of packets if they are still their default
values (i.e. not modified by the developer beforehand)
* Testpmd methods for port queue configuration and querying
Jeremy Spewock (4):
dts: add send_packets to test suites and rework packet addressing
dts: add port queue modification and forwarding stats to testpmd
dts: add dynamic queue test suite
dts: add dynamic queue conf to the yaml schema
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/framework/remote_session/testpmd_shell.py | 225 +++++++++++++-
dts/framework/test_suite.py | 78 +++--
dts/framework/testbed_model/tg_node.py | 9 +
dts/tests/TestSuite_dynamic_queue_conf.py | 287 ++++++++++++++++++
5 files changed, 578 insertions(+), 24 deletions(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
--
2.45.2
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v1 1/4] dts: add send_packets to test suites and rework packet addressing
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
@ 2024-06-25 15:53 ` jspewock
2024-06-25 15:53 ` [PATCH v1 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
` (6 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-06-25 15:53 UTC (permalink / raw)
To: Honnappa.Nagarahalli, wathsala.vithanage, npratte, thomas,
juraj.linkes, probb, Luca.Vizzarro, paul.szczepanek, yoan.picchi
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
Currently the only method provided in the test suite class for sending
packets sends a single packet and then captures the results. There is,
in some cases, a need to send multiple packets at once while not really
needing to capture any traffic received back. The method to do this
exists in the traffic generator already, but this patch exposes the
method to test suites.
This patch also updates the _adjust_addresses method of test suites so
that addresses of packets are only modified if the developer did not
configure them beforehand. This allows for developers to have more
control over the content of their packets when sending them through the
framework.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/test_suite.py | 78 +++++++++++++++++++-------
dts/framework/testbed_model/tg_node.py | 9 +++
2 files changed, 66 insertions(+), 21 deletions(-)
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 694b2eba65..6069025b2b 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -199,7 +199,7 @@ def send_packet_and_capture(
Returns:
A list of received packets.
"""
- packet = self._adjust_addresses(packet)
+ packet = self._adjust_addresses([packet])[0]
return self.tg_node.send_packet_and_capture(
packet,
self._tg_port_egress,
@@ -208,6 +208,18 @@ def send_packet_and_capture(
duration,
)
+ def send_packets(
+ self,
+ packets: list[Packet],
+ ) -> None:
+ """Send packets using the traffic generator and do not capture received traffic.
+
+ Args:
+ packets: Packets to send.
+ """
+ packets = self._adjust_addresses(packets)
+ self.tg_node.send_packets(packets, self._tg_port_egress)
+
def get_expected_packet(self, packet: Packet) -> Packet:
"""Inject the proper L2/L3 addresses into `packet`.
@@ -219,39 +231,63 @@ def get_expected_packet(self, packet: Packet) -> Packet:
"""
return self._adjust_addresses(packet, expected=True)
- def _adjust_addresses(self, packet: Packet, expected: bool = False) -> Packet:
+ def _adjust_addresses(self, packets: list[Packet], expected: bool = False) -> list[Packet]:
"""L2 and L3 address additions in both directions.
+ Only missing addresses are added to packets, existing addressed will not be overridden.
+
Assumptions:
Two links between SUT and TG, one link is TG -> SUT, the other SUT -> TG.
Args:
- packet: The packet to modify.
+ packets: The packets to modify.
expected: If :data:`True`, the direction is SUT -> TG,
otherwise the direction is TG -> SUT.
"""
- if expected:
- # The packet enters the TG from SUT
- # update l2 addresses
- packet.src = self._sut_port_egress.mac_address
- packet.dst = self._tg_port_ingress.mac_address
+ ret_packets = []
+ for packet in packets:
+ default_pkt_src = type(packet)().src
+ default_pkt_dst = type(packet)().dst
+ default_pkt_payload_src = (
+ type(packet.payload)().src if hasattr(packet.payload, "src") else None
+ )
+ default_pkt_payload_dst = (
+ type(packet.payload)().dst if hasattr(packet.payload, "dst") else None
+ )
+ # If `expected` is :data:`True`, the packet enters the TG from SUT, otherwise the
+ # packet leaves the TG towards the SUT
- # The packet is routed from TG egress to TG ingress
- # update l3 addresses
- packet.payload.src = self._tg_ip_address_egress.ip.exploded
- packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
- else:
- # The packet leaves TG towards SUT
# update l2 addresses
- packet.src = self._tg_port_egress.mac_address
- packet.dst = self._sut_port_ingress.mac_address
+ if packet.src == default_pkt_src:
+ packet.src = (
+ self._sut_port_egress.mac_address
+ if expected
+ else self._tg_port_egress.mac_address
+ )
+ if packet.dst == default_pkt_dst:
+ packet.dst = (
+ self._tg_port_ingress.mac_address
+ if expected
+ else self._sut_port_ingress.mac_address
+ )
+
+ # The packet is routed from TG egress to TG ingress regardless of if it is expected or
+ # not.
- # The packet is routed from TG egress to TG ingress
# update l3 addresses
- packet.payload.src = self._tg_ip_address_egress.ip.exploded
- packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
-
- return Ether(packet.build())
+ if (
+ default_pkt_payload_src is not None
+ and packet.payload.src == default_pkt_payload_src
+ ):
+ packet.payload.src = self._tg_ip_address_egress.ip.exploded
+ if (
+ default_pkt_payload_dst is not None
+ and packet.payload.dst == default_pkt_payload_dst
+ ):
+ packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
+ ret_packets.append(Ether(packet.build()))
+
+ return ret_packets
def verify(self, condition: bool, failure_description: str) -> None:
"""Verify `condition` and handle failures.
diff --git a/dts/framework/testbed_model/tg_node.py b/dts/framework/testbed_model/tg_node.py
index 4ee326e99c..758b676258 100644
--- a/dts/framework/testbed_model/tg_node.py
+++ b/dts/framework/testbed_model/tg_node.py
@@ -83,6 +83,15 @@ def send_packet_and_capture(
duration,
)
+ def send_packets(self, packets: list[Packet], port: Port):
+ """Send packets without capturing resulting received packets.
+
+ Args:
+ packets: Packets to send.
+ port: Port to send the packets on.
+ """
+ self.traffic_generator.send_packets(packets, port)
+
def close(self) -> None:
"""Free all resources used by the node.
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 2/4] dts: add port queue modification and forwarding stats to testpmd
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
2024-06-25 15:53 ` [PATCH v1 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
@ 2024-06-25 15:53 ` jspewock
2024-06-25 15:53 ` [PATCH v1 3/4] dts: add dynamic queue test suite jspewock
` (5 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-06-25 15:53 UTC (permalink / raw)
To: Honnappa.Nagarahalli, wathsala.vithanage, npratte, thomas,
juraj.linkes, probb, Luca.Vizzarro, paul.szczepanek, yoan.picchi
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds methods for querying and modifying port queue state and
configuration. In addition to this, it also adds the ability to capture
the forwarding statistics that get outputted when you send the "stop"
command in testpmd. Querying of port queue information is handled
through a TextParser dataclass in case there is future need for using
more of the output from the command used to query the information.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 225 +++++++++++++++++-
1 file changed, 223 insertions(+), 2 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index ec22f72221..e628d60dbe 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -19,7 +19,7 @@
from dataclasses import dataclass, field
from enum import Flag, auto
from pathlib import PurePath
-from typing import ClassVar
+from typing import ClassVar, cast
from typing_extensions import Self, Unpack
@@ -541,6 +541,48 @@ class TestPmdPort(TextParser):
)
+@dataclass
+class TestPmdPortQueue(TextParser):
+ """Dataclass representation of the common parts of the testpmd `show rxq/txq info` commands."""
+
+ #:
+ prefetch_threshold: int = field(metadata=TextParser.find_int(r"prefetch threshold: (\d+)"))
+ #:
+ host_threshold: int = field(metadata=TextParser.find_int(r"host threshold: (\d+)"))
+ #:
+ writeback_threshold: int = field(metadata=TextParser.find_int(r"writeback threshold: (\d+)"))
+ #:
+ free_threshold: int = field(metadata=TextParser.find_int(r"free threshold: (\d+)"))
+ #:
+ deferred_start: bool = field(metadata=TextParser.find("deferred start: on"))
+ #: The number of RXD/TXDs is just the ring size of the queue.
+ ring_size: int = field(metadata=TextParser.find_int(r"Number of (?:RXDs|TXDs): (\d+)"))
+ #:
+ is_queue_started: bool = field(metadata=TextParser.find("queue state: started"))
+ #:
+ burst_mode: str = field(metadata=TextParser.find(r"Burst mode: ([^\r\n]+)"))
+
+
+@dataclass
+class TestPmdTxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show txq info` command."""
+
+ #:
+ rs_threshold: int = field(metadata=TextParser.find_int(r"RS threshold: (\d+)"))
+
+
+@dataclass
+class TestPmdRxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show rxq info` command."""
+
+ #:
+ mempool: str = field(metadata=TextParser.find(r"Mempool: ([^\r\n]+)"))
+ #:
+ can_drop_packets: bool = field(metadata=TextParser.find(r"drop packets: on"))
+ #:
+ is_scattering_packets: bool = field(metadata=TextParser.find(r"scattered packets: on"))
+
+
@dataclass
class TestPmdPortStats(TextParser):
"""Port statistics."""
@@ -645,7 +687,7 @@ def start(self, verify: bool = True) -> None:
"Not all ports came up after starting packet forwarding in testpmd."
)
- def stop(self, verify: bool = True) -> None:
+ def stop(self, verify: bool = True) -> str:
"""Stop packet forwarding.
Args:
@@ -653,6 +695,9 @@ def stop(self, verify: bool = True) -> None:
forwarding was stopped successfully or not started. If neither is found, it is
considered an error.
+ Returns:
+ Output gathered from sending the stop command.
+
Raises:
InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
forwarding results in an error.
@@ -665,6 +710,7 @@ def stop(self, verify: bool = True) -> None:
):
self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}")
raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.")
+ return stop_cmd_output
def get_devices(self) -> list[TestPmdDevice]:
"""Get a list of device names that are known to testpmd.
@@ -806,6 +852,181 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
return TestPmdPortStats.parse(output)
+ def show_port_queue_info(
+ self, port_id: int, queue_id: int, is_rx_queue: bool
+ ) -> TestPmdPortQueue:
+ """Get the info for a queue on a given port.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+ is_rx_queue: Whether to check an RX or TX queue. If :data:`True` an RX queue will be
+ queried, otherwise a TX queue will be queried.
+
+ Raises:
+ InteractiveCommandExecutionError: If there is a failure when getting the info for the
+ queue.
+
+ Returns:
+ Information about the queue on the given port.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ queue_info = self.send_command(
+ f"show {queue_type} info {port_id} {queue_id}", skip_first_line=True
+ )
+ if queue_info.startswith("ETHDEV: Invalid"):
+ raise InteractiveCommandExecutionError(
+ f"Could not get the info for {queue_type} {queue_id} on port {port_id}"
+ )
+ return (
+ TestPmdRxPortQueue.parse(queue_info)
+ if is_rx_queue
+ else TestPmdTxPortQueue.parse(queue_info)
+ )
+
+ def show_port_rx_queue_info(self, port_id: int, queue_id: int) -> TestPmdRxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdRxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Rx queue on the given port.
+ """
+ return cast(TestPmdRxPortQueue, self.show_port_queue_info(port_id, queue_id, True))
+
+ def show_port_tx_queue_info(self, port_id: int, queue_id: int) -> TestPmdTxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdTxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Tx queue on the given port.
+ """
+ return cast(TestPmdTxPortQueue, self.show_port_queue_info(port_id, queue_id, False))
+
+ def stop_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Stops a given queue on a port.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to stop.
+ is_rx_queue: Type of queue to stop. If :data:`True` an RX queue will be stopped,
+ otherwise a TX queue will be stopped.
+ verify: If :data:`True` an additional command will be sent to verify the queue stopped.
+ Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ stop.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ stop_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} stop")
+ if verify:
+ if self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to stop {port_type} {queue_id} on port {port_id}"
+ )
+
+ def start_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Starts a given queue on a port.
+
+ First sets up the port queue, then starts it.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to start.
+ is_rx_queue: Type of queue to start. If :data:`True` an RX queue will be started,
+ otherwise a TX queue will be started.
+ verify: if :data:`True` an additional command will be sent to verify that the queue was
+ started. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ start.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ start_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} start")
+ if verify:
+ if not self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to start {port_type} {queue_id} on port {port_id}"
+ )
+
+ def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: bool) -> None:
+ """Setup a given queue on a port.
+
+ This functionality cannot be verified because the setup action only takes effect when the
+ queue is started.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to setup.
+ is_rx_queue: Type of queue to setup. If :data:`True` an RX queue will be setup,
+ otherwise a TX queue will be setup.
+ """
+ self.send_command(f"port {port_id} {'rxq' if is_rx_queue else 'txq'} {queue_id} setup")
+
+ def set_queue_ring_size(
+ self,
+ port_id: int,
+ queue_id: int,
+ size: int,
+ is_rx_queue: bool,
+ verify: bool = True,
+ ) -> None:
+ """Update the ring size of an Rx/Tx queue on a given port.
+
+ Queue is setup after setting the ring size so that the queue info reflects this change and
+ it can be verified.
+
+ Args:
+ port_id: The port that the queue resides on.
+ queue_id: The ID of the queue on the port.
+ size: The size to update the ring size to.
+ is_rx_queue: Whether to modify an RX or TX queue. If :data:`True` an RX queue will be
+ updated, otherwise a TX queue will be updated.
+ verify: If :data:`True` an additional command will be sent to check the ring size of
+ the queue in an attempt to validate that the size was changes properly.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and there is a failure
+ when updating ring size.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ self.send_command(f"port config {port_id} {queue_type} {queue_id} ring_size {size}")
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ if verify:
+ curr_ring_size = self.show_port_queue_info(port_id, queue_id, is_rx_queue).ring_size
+ if curr_ring_size != size:
+ self._logger.debug(
+ f"Failed up update ring size of queue {queue_id} on port {port_id}. Current"
+ f" ring size is {curr_ring_size}."
+ )
+ raise InteractiveCommandExecutionError(
+ f"Failed to update ring size of queue {queue_id} on port {port_id}"
+ )
+
def close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.send_command("quit", "")
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 3/4] dts: add dynamic queue test suite
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
2024-06-25 15:53 ` [PATCH v1 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
2024-06-25 15:53 ` [PATCH v1 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
@ 2024-06-25 15:53 ` jspewock
2024-06-25 15:53 ` [PATCH v1 4/4] dts: add dynamic queue conf to the yaml schema jspewock
` (4 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-06-25 15:53 UTC (permalink / raw)
To: Honnappa.Nagarahalli, wathsala.vithanage, npratte, thomas,
juraj.linkes, probb, Luca.Vizzarro, paul.szczepanek, yoan.picchi
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds a new test suite that is designed to test the stopping
and modification of port queues at runtime. Specifically, there are
test cases that display the ports ability to stop some queues but still
send and receive traffic on others, as well as the ability to configure
the ring size of the queue without blocking the traffic on other queues.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/tests/TestSuite_dynamic_queue_conf.py | 287 ++++++++++++++++++++++
1 file changed, 287 insertions(+)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
new file mode 100644
index 0000000000..6415593a0d
--- /dev/null
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -0,0 +1,287 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 University of New Hampshire
+
+"""Dynamic configuration of port queues test suite.
+
+This test suite tests the support of being able to either stop or reconfigure port queues at
+runtime without stopping the entire device. Previously, to configure a DPDK ethdev, the application
+first specifies how many Tx and Rx queues to include in the ethdev and then application sets up
+each queue individually. Only once all the queues have been set up can the application then start
+the device, and at this point traffic can flow. If device stops, this halts the flow of traffic on
+all queues in the ethdev completely. Dynamic queue is a capability present on some NICs that
+specifies whether the NIC is able to delay the configuration of queues on its port. This capability
+allows for the support of stopping and reconfiguring queues on a port at runtime without stopping
+the entire device.
+
+Support of this capability is shown by starting the Poll Mode Driver with multiple Rx and Tx queues
+configured and stopping some prior to forwarding packets, then examining whether or not the stopped
+ports and the unmodified ports were able to handle traffic. In addition to just stopping the ports,
+the ports must also show that they support configuration changes on their queues at runtime without
+stopping the entire device. This is shown by changing the ring size of the queues.
+
+If the Poll Mode Driver is able to stop some queues on a port and modify them then handle traffic
+on the unmodified queues while the others are stopped, then it is the case that the device properly
+supports dynamic configuration of its queues.
+"""
+
+import random
+from typing import Callable, ClassVar, MutableSet
+
+from scapy.layers.inet import IP # type: ignore[import-untyped]
+from scapy.layers.l2 import Ether # type: ignore[import-untyped]
+from scapy.packet import Raw # type: ignore[import-untyped]
+
+from framework.exception import InteractiveCommandExecutionError
+from framework.params.testpmd import PortTopology, SimpleForwardingModes
+from framework.remote_session.testpmd_shell import TestPmdShell
+from framework.test_suite import TestSuite
+
+
+def setup_and_teardown_test(
+ test_meth: Callable[
+ ["TestDynamicQueueConf", int, MutableSet, MutableSet, TestPmdShell, bool], None
+ ],
+) -> Callable[["TestDynamicQueueConf", bool], None]:
+ """Decorator that provides a setup and teardown for testing methods.
+
+ This decorator provides a method that sets up the environment for testing, runs the test
+ method, and then does a clean-up verification step after the queues are started again. The
+ decorated method will be provided with all the variables it should need to run testing
+ including: The ID of the port where the queues for testing reside, disjoint sets of IDs for
+ queues that are/aren't modified, a testpmd session to run testing with, and a flag that
+ indicates whether or not testing should be done on Rx or Tx queues.
+
+ Args:
+ test_meth: The decorated method that tests configuration of port queues at runtime.
+ This method must have the following parameters in order: An int that represents a
+ port ID, a set of queues for testing, a set of unmodified queues, a testpmd
+ interactive shell, and a boolean that, when :data:`True`, does Rx testing,
+ otherwise does Tx testing. This method must also be a member of the
+ :class:`TestDynamicQueueConf` class.
+
+ Returns:
+ A method that sets up the environment, runs the decorated method, then re-enables all
+ queues and validates they can still handle traffic.
+ """
+
+ def wrap(self: "TestDynamicQueueConf", is_rx_testing: bool) -> None:
+ """Setup environment, run test function, then cleanup.
+
+ Start a testpmd shell and stop ports for testing, then call the decorated function that
+ performs the testing. After the decorated function is finished running its testing,
+ start the stopped queues and send packets to validate that these ports can properly
+ handle traffic after being started again.
+
+ Args:
+ self: Instance of :class:`TestDynamicQueueConf` `test_meth` belongs to.
+ is_rx_testing: If :data:`True` then Rx queues will be the ones modified throughout
+ the test, otherwise Tx queues will be modified.
+ """
+ port_id = self.rx_port_num if is_rx_testing else self.tx_port_num
+ queues_to_config: set[int] = set()
+ while len(queues_to_config) < self.num_ports_to_modify:
+ queues_to_config.add(random.randint(1, self.number_of_queues - 1))
+ unchanged_queues = set(range(self.number_of_queues)) - queues_to_config
+ testpmd = TestPmdShell(
+ self.sut_node,
+ port_topology=PortTopology.chained,
+ rx_queues=self.number_of_queues,
+ tx_queues=self.number_of_queues,
+ )
+ for q in queues_to_config:
+ testpmd.stop_port_queue(port_id, q, is_rx_testing)
+ testpmd.set_forward_mode(SimpleForwardingModes.mac)
+
+ test_meth(self, port_id, queues_to_config, unchanged_queues, testpmd, is_rx_testing)
+
+ for queue_id in queues_to_config:
+ testpmd.start_port_queue(port_id, queue_id, is_rx_testing)
+
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+ for queue_id in queues_to_config:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, queue_id, forwarding_stats),
+ f"Modified queue {queue_id} on port {port_id} failed to receive traffic after"
+ "being started again.",
+ )
+ testpmd.close()
+
+ return wrap
+
+
+class TestDynamicQueueConf(TestSuite):
+ """DPDK dynamic queue configuration test suite.
+
+ Testing for the support of dynamic queue configuration is done by splitting testing by the type
+ of queue (either Rx or Tx) and the type of testing (testing for stopping a port at runtime vs
+ testing configuration changes at runtime). Testing is done by first stopping a finite number of
+ port queues (3 is sufficient) and either modifying the configuration or sending packets to
+ verify that the unmodified queues can handle traffic. Specifically, the following cases are
+ tested:
+
+ 1. The application should be able to start the device with only some of the
+ queues set up.
+ 2. The application should be able to reconfigure existing queues at runtime
+ without calling dev_stop().
+ """
+
+ #:
+ num_ports_to_modify: ClassVar[int] = 3
+ #: Source IP address to use when sending packets.
+ src_addr: ClassVar[str] = "192.168.0.1"
+ #: Subnet to use for all of the destination addresses of the packets being sent.
+ dst_address_subnet: ClassVar[str] = "192.168.1"
+ #: ID of the port to modify Rx queues on.
+ rx_port_num: ClassVar[int] = 0
+ #: ID of the port to modify Tx queues on.
+ tx_port_num: ClassVar[int] = 1
+ #: Number of queues to start testpmd with. There will be the same number of Rx and Tx queues.
+ #: 8 was chosen as a number that is low enough for most NICs to accommodate while also being
+ #: enough to validate the usage of the queues.
+ number_of_queues: ClassVar[int] = 8
+ #: The number of packets to send while testing. The test calls for well over the ring size - 1
+ #: packets in the modification test case and the only options for ring size are 256 or 512,
+ #: therefore 1024 will be more than enough.
+ number_of_packets_to_send: ClassVar[int] = 1024
+
+ def send_packets_with_different_addresses(self, number_of_packets: int) -> None:
+ """Send a set number of packets each with different dst addresses.
+
+ Different destination addresses are required to ensure that each queue is used. If every
+ packet had the same address, then they would all be processed by the same queue. Note that
+ this means the current implementation of this method is limited to only work for up to 254
+ queues. A smaller subnet would be required to handle an increased number of queues.
+
+ Args:
+ number_of_packets: The number of packets to generate and then send using the traffic
+ generator.
+ """
+ packets_to_send = [
+ Ether()
+ / IP(src=self.src_addr, dst=f"{self.dst_address_subnet}.{(i % 254) + 1}")
+ / Raw()
+ for i in range(number_of_packets)
+ ]
+ self.send_packets(packets_to_send)
+
+ def port_queue_in_stats(
+ self, port_id: int, is_rx_queue: bool, queue_id: int, stats: str
+ ) -> bool:
+ """Verify if stats for a queue are in the provided output.
+
+ Args:
+ port_id: ID of the port that the queue resides on.
+ is_rx_queue: Type of queue to scan for, if :data:`True` then search for an Rx queue,
+ otherwise search for a Tx queue.
+ queue_id: ID of the queue.
+ stats: Testpmd forwarding statistics to scan for the given queue.
+
+ Returns:
+ If the queue appeared in the forwarding statistics.
+ """
+ type_of_queue = "RX" if is_rx_queue else "TX"
+ return f"{type_of_queue} Port= {port_id}/Queue={queue_id:2d}" in stats
+
+ @setup_and_teardown_test
+ def modify_ring_size(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify ring size of port queues can be configured at runtime.
+
+ Ring size of queues in `queues_to_modify` are set to 512 unless that is already their
+ configured size, in which case they are instead set to 256. Queues in `queues_to_modify`
+ are expected to already be stopped before calling this method. `testpmd` is also expected
+ to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ for queue_id in queues_to_modify:
+ curr_ring_size = testpmd.show_port_queue_info(
+ port_id, queue_id, is_rx_testing
+ ).ring_size
+ new_ring_size = 256 if curr_ring_size == 512 else 512
+ try:
+ testpmd.set_queue_ring_size(
+ port_id, queue_id, new_ring_size, is_rx_testing, verify=True
+ )
+ # The testpmd method verifies that the modification worked, so we catch that error
+ # and just re-raise it as a test case failure
+ except InteractiveCommandExecutionError:
+ self.verify(
+ False,
+ f"Failed to update the ring size of queue {queue_id} on port "
+ f"{port_id} at runtime",
+ )
+
+ @setup_and_teardown_test
+ def stop_queues(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify stopped queues do not handle traffic and do not block traffic on other queues.
+
+ Queues in `queues_to_modify` are expected to already be stopped before calling this method.
+ `testpmd` is also expected to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+
+ # Checking that all unmodified queues handled some packets is important because this
+ # test case checks for the absence of stopped queues to validate that they cannot
+ # receive traffic. If there are some unchanged queues that also didn't receive traffic,
+ # it means there could be another reason for the packets not transmitting and,
+ # therefore, a false positive result.
+ for unchanged_q_id in unchanged_queues:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, unchanged_q_id, forwarding_stats),
+ f"Queue {unchanged_q_id} failed to receive traffic.",
+ )
+ for stopped_q_id in queues_to_modify:
+ self.verify(
+ not self.port_queue_in_stats(
+ port_id, is_rx_testing, stopped_q_id, forwarding_stats
+ ),
+ f"Queue {stopped_q_id} should be stopped but still received traffic.",
+ )
+
+ def test_rx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`True`."""
+ self.stop_queues(True)
+
+ def test_rx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`True`."""
+ self.modify_ring_size(True)
+
+ def test_tx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`False`."""
+ self.stop_queues(False)
+
+ def test_tx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`False`."""
+ self.modify_ring_size(False)
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 4/4] dts: add dynamic queue conf to the yaml schema
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
` (2 preceding siblings ...)
2024-06-25 15:53 ` [PATCH v1 3/4] dts: add dynamic queue test suite jspewock
@ 2024-06-25 15:53 ` jspewock
2024-07-03 21:58 ` [PATCH v2 0/4] dts: add dynamic queue configuration test suite jspewock
` (3 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-06-25 15:53 UTC (permalink / raw)
To: Honnappa.Nagarahalli, wathsala.vithanage, npratte, thomas,
juraj.linkes, probb, Luca.Vizzarro, paul.szczepanek, yoan.picchi
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
Adds the ability to run the test suite using the yaml configuration
file.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/config/conf_yaml_schema.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index f02a310bb5..d83a2f51c5 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -187,7 +187,8 @@
"enum": [
"hello_world",
"os_udp",
- "pmd_buffer_scatter"
+ "pmd_buffer_scatter",
+ "dynamic_queue_conf"
]
},
"test_target": {
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 0/4] dts: add dynamic queue configuration test suite
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
` (3 preceding siblings ...)
2024-06-25 15:53 ` [PATCH v1 4/4] dts: add dynamic queue conf to the yaml schema jspewock
@ 2024-07-03 21:58 ` jspewock
2024-07-03 21:58 ` [PATCH v2 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
` (3 more replies)
2024-07-24 15:07 ` [PATCH v3 0/4] dts: add dynamic queue configuration test suite jspewock
` (2 subsequent siblings)
7 siblings, 4 replies; 81+ messages in thread
From: jspewock @ 2024-07-03 21:58 UTC (permalink / raw)
To: npratte, Luca.Vizzarro, thomas, yoan.picchi, wathsala.vithanage,
probb, Honnappa.Nagarahalli, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v2:
* Fix default address in _adjust_addresses so that the position of the
IP layer of the payload does not matter
* Added default values to fields in the testpmd queue info. Burst mode
is not something that is always specified, so it had to be optional
and this means every field after it must also have a default.
Jeremy Spewock (4):
dts: add send_packets to test suites and rework packet addressing
dts: add port queue modification and forwarding stats to testpmd
dts: add dynamic queue test suite
dts: add dynamic queue conf to the yaml schema
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/framework/remote_session/testpmd_shell.py | 233 +++++++++++++-
dts/framework/test_suite.py | 74 +++--
dts/framework/testbed_model/tg_node.py | 9 +
dts/tests/TestSuite_dynamic_queue_conf.py | 287 ++++++++++++++++++
5 files changed, 582 insertions(+), 24 deletions(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
--
2.45.2
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v2 1/4] dts: add send_packets to test suites and rework packet addressing
2024-07-03 21:58 ` [PATCH v2 0/4] dts: add dynamic queue configuration test suite jspewock
@ 2024-07-03 21:58 ` jspewock
2024-07-03 21:58 ` [PATCH v2 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
` (2 subsequent siblings)
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-07-03 21:58 UTC (permalink / raw)
To: npratte, Luca.Vizzarro, thomas, yoan.picchi, wathsala.vithanage,
probb, Honnappa.Nagarahalli, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
Currently the only method provided in the test suite class for sending
packets sends a single packet and then captures the results. There is,
in some cases, a need to send multiple packets at once while not really
needing to capture any traffic received back. The method to do this
exists in the traffic generator already, but this patch exposes the
method to test suites.
This patch also updates the _adjust_addresses method of test suites so
that addresses of packets are only modified if the developer did not
configure them beforehand. This allows for developers to have more
control over the content of their packets when sending them through the
framework.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/test_suite.py | 74 ++++++++++++++++++--------
dts/framework/testbed_model/tg_node.py | 9 ++++
2 files changed, 62 insertions(+), 21 deletions(-)
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 694b2eba65..0b678ed62d 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -199,7 +199,7 @@ def send_packet_and_capture(
Returns:
A list of received packets.
"""
- packet = self._adjust_addresses(packet)
+ packet = self._adjust_addresses([packet])[0]
return self.tg_node.send_packet_and_capture(
packet,
self._tg_port_egress,
@@ -208,6 +208,18 @@ def send_packet_and_capture(
duration,
)
+ def send_packets(
+ self,
+ packets: list[Packet],
+ ) -> None:
+ """Send packets using the traffic generator and do not capture received traffic.
+
+ Args:
+ packets: Packets to send.
+ """
+ packets = self._adjust_addresses(packets)
+ self.tg_node.send_packets(packets, self._tg_port_egress)
+
def get_expected_packet(self, packet: Packet) -> Packet:
"""Inject the proper L2/L3 addresses into `packet`.
@@ -219,39 +231,59 @@ def get_expected_packet(self, packet: Packet) -> Packet:
"""
return self._adjust_addresses(packet, expected=True)
- def _adjust_addresses(self, packet: Packet, expected: bool = False) -> Packet:
+ def _adjust_addresses(self, packets: list[Packet], expected: bool = False) -> list[Packet]:
"""L2 and L3 address additions in both directions.
+ Only missing addresses are added to packets, existing addressed will not be overridden.
+
Assumptions:
Two links between SUT and TG, one link is TG -> SUT, the other SUT -> TG.
Args:
- packet: The packet to modify.
+ packets: The packets to modify.
expected: If :data:`True`, the direction is SUT -> TG,
otherwise the direction is TG -> SUT.
"""
- if expected:
- # The packet enters the TG from SUT
- # update l2 addresses
- packet.src = self._sut_port_egress.mac_address
- packet.dst = self._tg_port_ingress.mac_address
+ ret_packets = []
+ for packet in packets:
+ default_pkt_src = type(packet)().src
+ default_pkt_dst = type(packet)().dst
+ default_pkt_payload_src = IP().src if hasattr(packet.payload, "src") else None
+ default_pkt_payload_dst = IP().dst if hasattr(packet.payload, "dst") else None
+ # If `expected` is :data:`True`, the packet enters the TG from SUT, otherwise the
+ # packet leaves the TG towards the SUT
- # The packet is routed from TG egress to TG ingress
- # update l3 addresses
- packet.payload.src = self._tg_ip_address_egress.ip.exploded
- packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
- else:
- # The packet leaves TG towards SUT
# update l2 addresses
- packet.src = self._tg_port_egress.mac_address
- packet.dst = self._sut_port_ingress.mac_address
+ if packet.src == default_pkt_src:
+ packet.src = (
+ self._sut_port_egress.mac_address
+ if expected
+ else self._tg_port_egress.mac_address
+ )
+ if packet.dst == default_pkt_dst:
+ packet.dst = (
+ self._tg_port_ingress.mac_address
+ if expected
+ else self._sut_port_ingress.mac_address
+ )
+
+ # The packet is routed from TG egress to TG ingress regardless of if it is expected or
+ # not.
- # The packet is routed from TG egress to TG ingress
# update l3 addresses
- packet.payload.src = self._tg_ip_address_egress.ip.exploded
- packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
-
- return Ether(packet.build())
+ if (
+ default_pkt_payload_src is not None
+ and packet.payload.src == default_pkt_payload_src
+ ):
+ packet.payload.src = self._tg_ip_address_egress.ip.exploded
+ if (
+ default_pkt_payload_dst is not None
+ and packet.payload.dst == default_pkt_payload_dst
+ ):
+ packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
+ ret_packets.append(Ether(packet.build()))
+
+ return ret_packets
def verify(self, condition: bool, failure_description: str) -> None:
"""Verify `condition` and handle failures.
diff --git a/dts/framework/testbed_model/tg_node.py b/dts/framework/testbed_model/tg_node.py
index 4ee326e99c..758b676258 100644
--- a/dts/framework/testbed_model/tg_node.py
+++ b/dts/framework/testbed_model/tg_node.py
@@ -83,6 +83,15 @@ def send_packet_and_capture(
duration,
)
+ def send_packets(self, packets: list[Packet], port: Port):
+ """Send packets without capturing resulting received packets.
+
+ Args:
+ packets: Packets to send.
+ port: Port to send the packets on.
+ """
+ self.traffic_generator.send_packets(packets, port)
+
def close(self) -> None:
"""Free all resources used by the node.
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 2/4] dts: add port queue modification and forwarding stats to testpmd
2024-07-03 21:58 ` [PATCH v2 0/4] dts: add dynamic queue configuration test suite jspewock
2024-07-03 21:58 ` [PATCH v2 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
@ 2024-07-03 21:58 ` jspewock
2024-07-03 21:58 ` [PATCH v2 3/4] dts: add dynamic queue test suite jspewock
2024-07-03 21:58 ` [PATCH v2 4/4] dts: add dynamic queue conf to the yaml schema jspewock
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-07-03 21:58 UTC (permalink / raw)
To: npratte, Luca.Vizzarro, thomas, yoan.picchi, wathsala.vithanage,
probb, Honnappa.Nagarahalli, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds methods for querying and modifying port queue state and
configuration. In addition to this, it also adds the ability to capture
the forwarding statistics that get outputted when you send the "stop"
command in testpmd. Querying of port queue information is handled
through a TextParser dataclass in case there is future need for using
more of the output from the command used to query the information.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 233 +++++++++++++++++-
1 file changed, 231 insertions(+), 2 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index ec22f72221..74522790f5 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -19,7 +19,7 @@
from dataclasses import dataclass, field
from enum import Flag, auto
from pathlib import PurePath
-from typing import ClassVar
+from typing import ClassVar, cast
from typing_extensions import Self, Unpack
@@ -541,6 +541,56 @@ class TestPmdPort(TextParser):
)
+@dataclass
+class TestPmdPortQueue(TextParser):
+ """Dataclass representation of the common parts of the testpmd `show rxq/txq info` commands."""
+
+ #:
+ prefetch_threshold: int = field(metadata=TextParser.find_int(r"prefetch threshold: (\d+)"))
+ #:
+ host_threshold: int = field(metadata=TextParser.find_int(r"host threshold: (\d+)"))
+ #:
+ writeback_threshold: int = field(metadata=TextParser.find_int(r"writeback threshold: (\d+)"))
+ #:
+ free_threshold: int = field(metadata=TextParser.find_int(r"free threshold: (\d+)"))
+ #:
+ deferred_start: bool = field(metadata=TextParser.find("deferred start: on"))
+ #: The number of RXD/TXDs is just the ring size of the queue.
+ ring_size: int = field(metadata=TextParser.find_int(r"Number of (?:RXDs|TXDs): (\d+)"))
+ #:
+ is_queue_started: bool = field(metadata=TextParser.find("queue state: started"))
+ #:
+ burst_mode: str | None = field(
+ default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)")
+ )
+
+
+@dataclass
+class TestPmdTxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show txq info` command."""
+
+ #:
+ rs_threshold: int | None = field(
+ default=None, metadata=TextParser.find_int(r"RS threshold: (\d+)")
+ )
+
+
+@dataclass
+class TestPmdRxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show rxq info` command."""
+
+ #:
+ mempool: str | None = field(default=None, metadata=TextParser.find(r"Mempool: ([^\r\n]+)"))
+ #:
+ can_drop_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"drop packets: on")
+ )
+ #:
+ is_scattering_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"scattered packets: on")
+ )
+
+
@dataclass
class TestPmdPortStats(TextParser):
"""Port statistics."""
@@ -645,7 +695,7 @@ def start(self, verify: bool = True) -> None:
"Not all ports came up after starting packet forwarding in testpmd."
)
- def stop(self, verify: bool = True) -> None:
+ def stop(self, verify: bool = True) -> str:
"""Stop packet forwarding.
Args:
@@ -653,6 +703,9 @@ def stop(self, verify: bool = True) -> None:
forwarding was stopped successfully or not started. If neither is found, it is
considered an error.
+ Returns:
+ Output gathered from sending the stop command.
+
Raises:
InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
forwarding results in an error.
@@ -665,6 +718,7 @@ def stop(self, verify: bool = True) -> None:
):
self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}")
raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.")
+ return stop_cmd_output
def get_devices(self) -> list[TestPmdDevice]:
"""Get a list of device names that are known to testpmd.
@@ -806,6 +860,181 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
return TestPmdPortStats.parse(output)
+ def show_port_queue_info(
+ self, port_id: int, queue_id: int, is_rx_queue: bool
+ ) -> TestPmdPortQueue:
+ """Get the info for a queue on a given port.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+ is_rx_queue: Whether to check an RX or TX queue. If :data:`True` an RX queue will be
+ queried, otherwise a TX queue will be queried.
+
+ Raises:
+ InteractiveCommandExecutionError: If there is a failure when getting the info for the
+ queue.
+
+ Returns:
+ Information about the queue on the given port.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ queue_info = self.send_command(
+ f"show {queue_type} info {port_id} {queue_id}", skip_first_line=True
+ )
+ if queue_info.startswith("ETHDEV: Invalid"):
+ raise InteractiveCommandExecutionError(
+ f"Could not get the info for {queue_type} {queue_id} on port {port_id}"
+ )
+ return (
+ TestPmdRxPortQueue.parse(queue_info)
+ if is_rx_queue
+ else TestPmdTxPortQueue.parse(queue_info)
+ )
+
+ def show_port_rx_queue_info(self, port_id: int, queue_id: int) -> TestPmdRxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdRxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Rx queue on the given port.
+ """
+ return cast(TestPmdRxPortQueue, self.show_port_queue_info(port_id, queue_id, True))
+
+ def show_port_tx_queue_info(self, port_id: int, queue_id: int) -> TestPmdTxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdTxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Tx queue on the given port.
+ """
+ return cast(TestPmdTxPortQueue, self.show_port_queue_info(port_id, queue_id, False))
+
+ def stop_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Stops a given queue on a port.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to stop.
+ is_rx_queue: Type of queue to stop. If :data:`True` an RX queue will be stopped,
+ otherwise a TX queue will be stopped.
+ verify: If :data:`True` an additional command will be sent to verify the queue stopped.
+ Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ stop.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ stop_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} stop")
+ if verify:
+ if self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to stop {port_type} {queue_id} on port {port_id}"
+ )
+
+ def start_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Starts a given queue on a port.
+
+ First sets up the port queue, then starts it.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to start.
+ is_rx_queue: Type of queue to start. If :data:`True` an RX queue will be started,
+ otherwise a TX queue will be started.
+ verify: if :data:`True` an additional command will be sent to verify that the queue was
+ started. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ start.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ start_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} start")
+ if verify:
+ if not self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to start {port_type} {queue_id} on port {port_id}"
+ )
+
+ def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: bool) -> None:
+ """Setup a given queue on a port.
+
+ This functionality cannot be verified because the setup action only takes effect when the
+ queue is started.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to setup.
+ is_rx_queue: Type of queue to setup. If :data:`True` an RX queue will be setup,
+ otherwise a TX queue will be setup.
+ """
+ self.send_command(f"port {port_id} {'rxq' if is_rx_queue else 'txq'} {queue_id} setup")
+
+ def set_queue_ring_size(
+ self,
+ port_id: int,
+ queue_id: int,
+ size: int,
+ is_rx_queue: bool,
+ verify: bool = True,
+ ) -> None:
+ """Update the ring size of an Rx/Tx queue on a given port.
+
+ Queue is setup after setting the ring size so that the queue info reflects this change and
+ it can be verified.
+
+ Args:
+ port_id: The port that the queue resides on.
+ queue_id: The ID of the queue on the port.
+ size: The size to update the ring size to.
+ is_rx_queue: Whether to modify an RX or TX queue. If :data:`True` an RX queue will be
+ updated, otherwise a TX queue will be updated.
+ verify: If :data:`True` an additional command will be sent to check the ring size of
+ the queue in an attempt to validate that the size was changes properly.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and there is a failure
+ when updating ring size.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ self.send_command(f"port config {port_id} {queue_type} {queue_id} ring_size {size}")
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ if verify:
+ curr_ring_size = self.show_port_queue_info(port_id, queue_id, is_rx_queue).ring_size
+ if curr_ring_size != size:
+ self._logger.debug(
+ f"Failed up update ring size of queue {queue_id} on port {port_id}. Current"
+ f" ring size is {curr_ring_size}."
+ )
+ raise InteractiveCommandExecutionError(
+ f"Failed to update ring size of queue {queue_id} on port {port_id}"
+ )
+
def close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.send_command("quit", "")
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 3/4] dts: add dynamic queue test suite
2024-07-03 21:58 ` [PATCH v2 0/4] dts: add dynamic queue configuration test suite jspewock
2024-07-03 21:58 ` [PATCH v2 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
2024-07-03 21:58 ` [PATCH v2 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
@ 2024-07-03 21:58 ` jspewock
2024-07-03 21:58 ` [PATCH v2 4/4] dts: add dynamic queue conf to the yaml schema jspewock
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-07-03 21:58 UTC (permalink / raw)
To: npratte, Luca.Vizzarro, thomas, yoan.picchi, wathsala.vithanage,
probb, Honnappa.Nagarahalli, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds a new test suite that is designed to test the stopping
and modification of port queues at runtime. Specifically, there are
test cases that display the ports ability to stop some queues but still
send and receive traffic on others, as well as the ability to configure
the ring size of the queue without blocking the traffic on other queues.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/tests/TestSuite_dynamic_queue_conf.py | 287 ++++++++++++++++++++++
1 file changed, 287 insertions(+)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
new file mode 100644
index 0000000000..6415593a0d
--- /dev/null
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -0,0 +1,287 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 University of New Hampshire
+
+"""Dynamic configuration of port queues test suite.
+
+This test suite tests the support of being able to either stop or reconfigure port queues at
+runtime without stopping the entire device. Previously, to configure a DPDK ethdev, the application
+first specifies how many Tx and Rx queues to include in the ethdev and then application sets up
+each queue individually. Only once all the queues have been set up can the application then start
+the device, and at this point traffic can flow. If device stops, this halts the flow of traffic on
+all queues in the ethdev completely. Dynamic queue is a capability present on some NICs that
+specifies whether the NIC is able to delay the configuration of queues on its port. This capability
+allows for the support of stopping and reconfiguring queues on a port at runtime without stopping
+the entire device.
+
+Support of this capability is shown by starting the Poll Mode Driver with multiple Rx and Tx queues
+configured and stopping some prior to forwarding packets, then examining whether or not the stopped
+ports and the unmodified ports were able to handle traffic. In addition to just stopping the ports,
+the ports must also show that they support configuration changes on their queues at runtime without
+stopping the entire device. This is shown by changing the ring size of the queues.
+
+If the Poll Mode Driver is able to stop some queues on a port and modify them then handle traffic
+on the unmodified queues while the others are stopped, then it is the case that the device properly
+supports dynamic configuration of its queues.
+"""
+
+import random
+from typing import Callable, ClassVar, MutableSet
+
+from scapy.layers.inet import IP # type: ignore[import-untyped]
+from scapy.layers.l2 import Ether # type: ignore[import-untyped]
+from scapy.packet import Raw # type: ignore[import-untyped]
+
+from framework.exception import InteractiveCommandExecutionError
+from framework.params.testpmd import PortTopology, SimpleForwardingModes
+from framework.remote_session.testpmd_shell import TestPmdShell
+from framework.test_suite import TestSuite
+
+
+def setup_and_teardown_test(
+ test_meth: Callable[
+ ["TestDynamicQueueConf", int, MutableSet, MutableSet, TestPmdShell, bool], None
+ ],
+) -> Callable[["TestDynamicQueueConf", bool], None]:
+ """Decorator that provides a setup and teardown for testing methods.
+
+ This decorator provides a method that sets up the environment for testing, runs the test
+ method, and then does a clean-up verification step after the queues are started again. The
+ decorated method will be provided with all the variables it should need to run testing
+ including: The ID of the port where the queues for testing reside, disjoint sets of IDs for
+ queues that are/aren't modified, a testpmd session to run testing with, and a flag that
+ indicates whether or not testing should be done on Rx or Tx queues.
+
+ Args:
+ test_meth: The decorated method that tests configuration of port queues at runtime.
+ This method must have the following parameters in order: An int that represents a
+ port ID, a set of queues for testing, a set of unmodified queues, a testpmd
+ interactive shell, and a boolean that, when :data:`True`, does Rx testing,
+ otherwise does Tx testing. This method must also be a member of the
+ :class:`TestDynamicQueueConf` class.
+
+ Returns:
+ A method that sets up the environment, runs the decorated method, then re-enables all
+ queues and validates they can still handle traffic.
+ """
+
+ def wrap(self: "TestDynamicQueueConf", is_rx_testing: bool) -> None:
+ """Setup environment, run test function, then cleanup.
+
+ Start a testpmd shell and stop ports for testing, then call the decorated function that
+ performs the testing. After the decorated function is finished running its testing,
+ start the stopped queues and send packets to validate that these ports can properly
+ handle traffic after being started again.
+
+ Args:
+ self: Instance of :class:`TestDynamicQueueConf` `test_meth` belongs to.
+ is_rx_testing: If :data:`True` then Rx queues will be the ones modified throughout
+ the test, otherwise Tx queues will be modified.
+ """
+ port_id = self.rx_port_num if is_rx_testing else self.tx_port_num
+ queues_to_config: set[int] = set()
+ while len(queues_to_config) < self.num_ports_to_modify:
+ queues_to_config.add(random.randint(1, self.number_of_queues - 1))
+ unchanged_queues = set(range(self.number_of_queues)) - queues_to_config
+ testpmd = TestPmdShell(
+ self.sut_node,
+ port_topology=PortTopology.chained,
+ rx_queues=self.number_of_queues,
+ tx_queues=self.number_of_queues,
+ )
+ for q in queues_to_config:
+ testpmd.stop_port_queue(port_id, q, is_rx_testing)
+ testpmd.set_forward_mode(SimpleForwardingModes.mac)
+
+ test_meth(self, port_id, queues_to_config, unchanged_queues, testpmd, is_rx_testing)
+
+ for queue_id in queues_to_config:
+ testpmd.start_port_queue(port_id, queue_id, is_rx_testing)
+
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+ for queue_id in queues_to_config:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, queue_id, forwarding_stats),
+ f"Modified queue {queue_id} on port {port_id} failed to receive traffic after"
+ "being started again.",
+ )
+ testpmd.close()
+
+ return wrap
+
+
+class TestDynamicQueueConf(TestSuite):
+ """DPDK dynamic queue configuration test suite.
+
+ Testing for the support of dynamic queue configuration is done by splitting testing by the type
+ of queue (either Rx or Tx) and the type of testing (testing for stopping a port at runtime vs
+ testing configuration changes at runtime). Testing is done by first stopping a finite number of
+ port queues (3 is sufficient) and either modifying the configuration or sending packets to
+ verify that the unmodified queues can handle traffic. Specifically, the following cases are
+ tested:
+
+ 1. The application should be able to start the device with only some of the
+ queues set up.
+ 2. The application should be able to reconfigure existing queues at runtime
+ without calling dev_stop().
+ """
+
+ #:
+ num_ports_to_modify: ClassVar[int] = 3
+ #: Source IP address to use when sending packets.
+ src_addr: ClassVar[str] = "192.168.0.1"
+ #: Subnet to use for all of the destination addresses of the packets being sent.
+ dst_address_subnet: ClassVar[str] = "192.168.1"
+ #: ID of the port to modify Rx queues on.
+ rx_port_num: ClassVar[int] = 0
+ #: ID of the port to modify Tx queues on.
+ tx_port_num: ClassVar[int] = 1
+ #: Number of queues to start testpmd with. There will be the same number of Rx and Tx queues.
+ #: 8 was chosen as a number that is low enough for most NICs to accommodate while also being
+ #: enough to validate the usage of the queues.
+ number_of_queues: ClassVar[int] = 8
+ #: The number of packets to send while testing. The test calls for well over the ring size - 1
+ #: packets in the modification test case and the only options for ring size are 256 or 512,
+ #: therefore 1024 will be more than enough.
+ number_of_packets_to_send: ClassVar[int] = 1024
+
+ def send_packets_with_different_addresses(self, number_of_packets: int) -> None:
+ """Send a set number of packets each with different dst addresses.
+
+ Different destination addresses are required to ensure that each queue is used. If every
+ packet had the same address, then they would all be processed by the same queue. Note that
+ this means the current implementation of this method is limited to only work for up to 254
+ queues. A smaller subnet would be required to handle an increased number of queues.
+
+ Args:
+ number_of_packets: The number of packets to generate and then send using the traffic
+ generator.
+ """
+ packets_to_send = [
+ Ether()
+ / IP(src=self.src_addr, dst=f"{self.dst_address_subnet}.{(i % 254) + 1}")
+ / Raw()
+ for i in range(number_of_packets)
+ ]
+ self.send_packets(packets_to_send)
+
+ def port_queue_in_stats(
+ self, port_id: int, is_rx_queue: bool, queue_id: int, stats: str
+ ) -> bool:
+ """Verify if stats for a queue are in the provided output.
+
+ Args:
+ port_id: ID of the port that the queue resides on.
+ is_rx_queue: Type of queue to scan for, if :data:`True` then search for an Rx queue,
+ otherwise search for a Tx queue.
+ queue_id: ID of the queue.
+ stats: Testpmd forwarding statistics to scan for the given queue.
+
+ Returns:
+ If the queue appeared in the forwarding statistics.
+ """
+ type_of_queue = "RX" if is_rx_queue else "TX"
+ return f"{type_of_queue} Port= {port_id}/Queue={queue_id:2d}" in stats
+
+ @setup_and_teardown_test
+ def modify_ring_size(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify ring size of port queues can be configured at runtime.
+
+ Ring size of queues in `queues_to_modify` are set to 512 unless that is already their
+ configured size, in which case they are instead set to 256. Queues in `queues_to_modify`
+ are expected to already be stopped before calling this method. `testpmd` is also expected
+ to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ for queue_id in queues_to_modify:
+ curr_ring_size = testpmd.show_port_queue_info(
+ port_id, queue_id, is_rx_testing
+ ).ring_size
+ new_ring_size = 256 if curr_ring_size == 512 else 512
+ try:
+ testpmd.set_queue_ring_size(
+ port_id, queue_id, new_ring_size, is_rx_testing, verify=True
+ )
+ # The testpmd method verifies that the modification worked, so we catch that error
+ # and just re-raise it as a test case failure
+ except InteractiveCommandExecutionError:
+ self.verify(
+ False,
+ f"Failed to update the ring size of queue {queue_id} on port "
+ f"{port_id} at runtime",
+ )
+
+ @setup_and_teardown_test
+ def stop_queues(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify stopped queues do not handle traffic and do not block traffic on other queues.
+
+ Queues in `queues_to_modify` are expected to already be stopped before calling this method.
+ `testpmd` is also expected to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+
+ # Checking that all unmodified queues handled some packets is important because this
+ # test case checks for the absence of stopped queues to validate that they cannot
+ # receive traffic. If there are some unchanged queues that also didn't receive traffic,
+ # it means there could be another reason for the packets not transmitting and,
+ # therefore, a false positive result.
+ for unchanged_q_id in unchanged_queues:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, unchanged_q_id, forwarding_stats),
+ f"Queue {unchanged_q_id} failed to receive traffic.",
+ )
+ for stopped_q_id in queues_to_modify:
+ self.verify(
+ not self.port_queue_in_stats(
+ port_id, is_rx_testing, stopped_q_id, forwarding_stats
+ ),
+ f"Queue {stopped_q_id} should be stopped but still received traffic.",
+ )
+
+ def test_rx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`True`."""
+ self.stop_queues(True)
+
+ def test_rx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`True`."""
+ self.modify_ring_size(True)
+
+ def test_tx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`False`."""
+ self.stop_queues(False)
+
+ def test_tx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`False`."""
+ self.modify_ring_size(False)
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 4/4] dts: add dynamic queue conf to the yaml schema
2024-07-03 21:58 ` [PATCH v2 0/4] dts: add dynamic queue configuration test suite jspewock
` (2 preceding siblings ...)
2024-07-03 21:58 ` [PATCH v2 3/4] dts: add dynamic queue test suite jspewock
@ 2024-07-03 21:58 ` jspewock
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-07-03 21:58 UTC (permalink / raw)
To: npratte, Luca.Vizzarro, thomas, yoan.picchi, wathsala.vithanage,
probb, Honnappa.Nagarahalli, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
Adds the ability to run the test suite using the yaml configuration
file.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/config/conf_yaml_schema.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index f02a310bb5..d83a2f51c5 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -187,7 +187,8 @@
"enum": [
"hello_world",
"os_udp",
- "pmd_buffer_scatter"
+ "pmd_buffer_scatter",
+ "dynamic_queue_conf"
]
},
"test_target": {
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 0/4] dts: add dynamic queue configuration test suite
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
` (4 preceding siblings ...)
2024-07-03 21:58 ` [PATCH v2 0/4] dts: add dynamic queue configuration test suite jspewock
@ 2024-07-24 15:07 ` jspewock
2024-07-24 15:07 ` [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
` (3 more replies)
2024-09-04 15:49 ` [PATCH v4 0/2] dts: add dynamic queue configuration test suite jspewock
2024-09-25 19:20 ` [PATCH v5 0/2] dts: add dynamic queue configuration " jspewock
7 siblings, 4 replies; 81+ messages in thread
From: jspewock @ 2024-07-24 15:07 UTC (permalink / raw)
To: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
npratte, wathsala.vithanage, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v3:
* rebase on rc3
Jeremy Spewock (4):
dts: add send_packets to test suites and rework packet addressing
dts: add port queue modification and forwarding stats to testpmd
dts: add dynamic queue test suite
dts: add dynamic queue conf to the yaml schema
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/framework/remote_session/testpmd_shell.py | 233 +++++++++++++-
dts/framework/test_suite.py | 74 +++--
dts/framework/testbed_model/tg_node.py | 9 +
dts/tests/TestSuite_dynamic_queue_conf.py | 286 ++++++++++++++++++
5 files changed, 581 insertions(+), 24 deletions(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
--
2.45.2
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing
2024-07-24 15:07 ` [PATCH v3 0/4] dts: add dynamic queue configuration test suite jspewock
@ 2024-07-24 15:07 ` jspewock
2024-07-26 14:37 ` Nicholas Pratte
2024-07-26 19:00 ` Nicholas Pratte
2024-07-24 15:07 ` [PATCH v3 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
` (2 subsequent siblings)
3 siblings, 2 replies; 81+ messages in thread
From: jspewock @ 2024-07-24 15:07 UTC (permalink / raw)
To: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
npratte, wathsala.vithanage, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
Currently the only method provided in the test suite class for sending
packets sends a single packet and then captures the results. There is,
in some cases, a need to send multiple packets at once while not really
needing to capture any traffic received back. The method to do this
exists in the traffic generator already, but this patch exposes the
method to test suites.
This patch also updates the _adjust_addresses method of test suites so
that addresses of packets are only modified if the developer did not
configure them beforehand. This allows for developers to have more
control over the content of their packets when sending them through the
framework.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/test_suite.py | 74 ++++++++++++++++++--------
dts/framework/testbed_model/tg_node.py | 9 ++++
2 files changed, 62 insertions(+), 21 deletions(-)
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 694b2eba65..0b678ed62d 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -199,7 +199,7 @@ def send_packet_and_capture(
Returns:
A list of received packets.
"""
- packet = self._adjust_addresses(packet)
+ packet = self._adjust_addresses([packet])[0]
return self.tg_node.send_packet_and_capture(
packet,
self._tg_port_egress,
@@ -208,6 +208,18 @@ def send_packet_and_capture(
duration,
)
+ def send_packets(
+ self,
+ packets: list[Packet],
+ ) -> None:
+ """Send packets using the traffic generator and do not capture received traffic.
+
+ Args:
+ packets: Packets to send.
+ """
+ packets = self._adjust_addresses(packets)
+ self.tg_node.send_packets(packets, self._tg_port_egress)
+
def get_expected_packet(self, packet: Packet) -> Packet:
"""Inject the proper L2/L3 addresses into `packet`.
@@ -219,39 +231,59 @@ def get_expected_packet(self, packet: Packet) -> Packet:
"""
return self._adjust_addresses(packet, expected=True)
- def _adjust_addresses(self, packet: Packet, expected: bool = False) -> Packet:
+ def _adjust_addresses(self, packets: list[Packet], expected: bool = False) -> list[Packet]:
"""L2 and L3 address additions in both directions.
+ Only missing addresses are added to packets, existing addressed will not be overridden.
+
Assumptions:
Two links between SUT and TG, one link is TG -> SUT, the other SUT -> TG.
Args:
- packet: The packet to modify.
+ packets: The packets to modify.
expected: If :data:`True`, the direction is SUT -> TG,
otherwise the direction is TG -> SUT.
"""
- if expected:
- # The packet enters the TG from SUT
- # update l2 addresses
- packet.src = self._sut_port_egress.mac_address
- packet.dst = self._tg_port_ingress.mac_address
+ ret_packets = []
+ for packet in packets:
+ default_pkt_src = type(packet)().src
+ default_pkt_dst = type(packet)().dst
+ default_pkt_payload_src = IP().src if hasattr(packet.payload, "src") else None
+ default_pkt_payload_dst = IP().dst if hasattr(packet.payload, "dst") else None
+ # If `expected` is :data:`True`, the packet enters the TG from SUT, otherwise the
+ # packet leaves the TG towards the SUT
- # The packet is routed from TG egress to TG ingress
- # update l3 addresses
- packet.payload.src = self._tg_ip_address_egress.ip.exploded
- packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
- else:
- # The packet leaves TG towards SUT
# update l2 addresses
- packet.src = self._tg_port_egress.mac_address
- packet.dst = self._sut_port_ingress.mac_address
+ if packet.src == default_pkt_src:
+ packet.src = (
+ self._sut_port_egress.mac_address
+ if expected
+ else self._tg_port_egress.mac_address
+ )
+ if packet.dst == default_pkt_dst:
+ packet.dst = (
+ self._tg_port_ingress.mac_address
+ if expected
+ else self._sut_port_ingress.mac_address
+ )
+
+ # The packet is routed from TG egress to TG ingress regardless of if it is expected or
+ # not.
- # The packet is routed from TG egress to TG ingress
# update l3 addresses
- packet.payload.src = self._tg_ip_address_egress.ip.exploded
- packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
-
- return Ether(packet.build())
+ if (
+ default_pkt_payload_src is not None
+ and packet.payload.src == default_pkt_payload_src
+ ):
+ packet.payload.src = self._tg_ip_address_egress.ip.exploded
+ if (
+ default_pkt_payload_dst is not None
+ and packet.payload.dst == default_pkt_payload_dst
+ ):
+ packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
+ ret_packets.append(Ether(packet.build()))
+
+ return ret_packets
def verify(self, condition: bool, failure_description: str) -> None:
"""Verify `condition` and handle failures.
diff --git a/dts/framework/testbed_model/tg_node.py b/dts/framework/testbed_model/tg_node.py
index 4ee326e99c..758b676258 100644
--- a/dts/framework/testbed_model/tg_node.py
+++ b/dts/framework/testbed_model/tg_node.py
@@ -83,6 +83,15 @@ def send_packet_and_capture(
duration,
)
+ def send_packets(self, packets: list[Packet], port: Port):
+ """Send packets without capturing resulting received packets.
+
+ Args:
+ packets: Packets to send.
+ port: Port to send the packets on.
+ """
+ self.traffic_generator.send_packets(packets, port)
+
def close(self) -> None:
"""Free all resources used by the node.
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 2/4] dts: add port queue modification and forwarding stats to testpmd
2024-07-24 15:07 ` [PATCH v3 0/4] dts: add dynamic queue configuration test suite jspewock
2024-07-24 15:07 ` [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
@ 2024-07-24 15:07 ` jspewock
2024-07-24 15:07 ` [PATCH v3 3/4] dts: add dynamic queue test suite jspewock
2024-07-24 15:07 ` [PATCH v3 4/4] dts: add dynamic queue conf to the yaml schema jspewock
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-07-24 15:07 UTC (permalink / raw)
To: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
npratte, wathsala.vithanage, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds methods for querying and modifying port queue state and
configuration. In addition to this, it also adds the ability to capture
the forwarding statistics that get outputted when you send the "stop"
command in testpmd. Querying of port queue information is handled
through a TextParser dataclass in case there is future need for using
more of the output from the command used to query the information.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 233 +++++++++++++++++-
1 file changed, 231 insertions(+), 2 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index eda6eb320f..45b379c808 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -19,7 +19,7 @@
from dataclasses import dataclass, field
from enum import Flag, auto
from pathlib import PurePath
-from typing import ClassVar
+from typing import ClassVar, cast
from typing_extensions import Self, Unpack
@@ -541,6 +541,56 @@ class TestPmdPort(TextParser):
)
+@dataclass
+class TestPmdPortQueue(TextParser):
+ """Dataclass representation of the common parts of the testpmd `show rxq/txq info` commands."""
+
+ #:
+ prefetch_threshold: int = field(metadata=TextParser.find_int(r"prefetch threshold: (\d+)"))
+ #:
+ host_threshold: int = field(metadata=TextParser.find_int(r"host threshold: (\d+)"))
+ #:
+ writeback_threshold: int = field(metadata=TextParser.find_int(r"writeback threshold: (\d+)"))
+ #:
+ free_threshold: int = field(metadata=TextParser.find_int(r"free threshold: (\d+)"))
+ #:
+ deferred_start: bool = field(metadata=TextParser.find("deferred start: on"))
+ #: The number of RXD/TXDs is just the ring size of the queue.
+ ring_size: int = field(metadata=TextParser.find_int(r"Number of (?:RXDs|TXDs): (\d+)"))
+ #:
+ is_queue_started: bool = field(metadata=TextParser.find("queue state: started"))
+ #:
+ burst_mode: str | None = field(
+ default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)")
+ )
+
+
+@dataclass
+class TestPmdTxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show txq info` command."""
+
+ #:
+ rs_threshold: int | None = field(
+ default=None, metadata=TextParser.find_int(r"RS threshold: (\d+)")
+ )
+
+
+@dataclass
+class TestPmdRxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show rxq info` command."""
+
+ #:
+ mempool: str | None = field(default=None, metadata=TextParser.find(r"Mempool: ([^\r\n]+)"))
+ #:
+ can_drop_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"drop packets: on")
+ )
+ #:
+ is_scattering_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"scattered packets: on")
+ )
+
+
@dataclass
class TestPmdPortStats(TextParser):
"""Port statistics."""
@@ -643,7 +693,7 @@ def start(self, verify: bool = True) -> None:
"Not all ports came up after starting packet forwarding in testpmd."
)
- def stop(self, verify: bool = True) -> None:
+ def stop(self, verify: bool = True) -> str:
"""Stop packet forwarding.
Args:
@@ -651,6 +701,9 @@ def stop(self, verify: bool = True) -> None:
forwarding was stopped successfully or not started. If neither is found, it is
considered an error.
+ Returns:
+ Output gathered from sending the stop command.
+
Raises:
InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
forwarding results in an error.
@@ -663,6 +716,7 @@ def stop(self, verify: bool = True) -> None:
):
self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}")
raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.")
+ return stop_cmd_output
def get_devices(self) -> list[TestPmdDevice]:
"""Get a list of device names that are known to testpmd.
@@ -804,6 +858,181 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
return TestPmdPortStats.parse(output)
+ def show_port_queue_info(
+ self, port_id: int, queue_id: int, is_rx_queue: bool
+ ) -> TestPmdPortQueue:
+ """Get the info for a queue on a given port.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+ is_rx_queue: Whether to check an RX or TX queue. If :data:`True` an RX queue will be
+ queried, otherwise a TX queue will be queried.
+
+ Raises:
+ InteractiveCommandExecutionError: If there is a failure when getting the info for the
+ queue.
+
+ Returns:
+ Information about the queue on the given port.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ queue_info = self.send_command(
+ f"show {queue_type} info {port_id} {queue_id}", skip_first_line=True
+ )
+ if queue_info.startswith("ETHDEV: Invalid"):
+ raise InteractiveCommandExecutionError(
+ f"Could not get the info for {queue_type} {queue_id} on port {port_id}"
+ )
+ return (
+ TestPmdRxPortQueue.parse(queue_info)
+ if is_rx_queue
+ else TestPmdTxPortQueue.parse(queue_info)
+ )
+
+ def show_port_rx_queue_info(self, port_id: int, queue_id: int) -> TestPmdRxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdRxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Rx queue on the given port.
+ """
+ return cast(TestPmdRxPortQueue, self.show_port_queue_info(port_id, queue_id, True))
+
+ def show_port_tx_queue_info(self, port_id: int, queue_id: int) -> TestPmdTxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdTxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Tx queue on the given port.
+ """
+ return cast(TestPmdTxPortQueue, self.show_port_queue_info(port_id, queue_id, False))
+
+ def stop_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Stops a given queue on a port.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to stop.
+ is_rx_queue: Type of queue to stop. If :data:`True` an RX queue will be stopped,
+ otherwise a TX queue will be stopped.
+ verify: If :data:`True` an additional command will be sent to verify the queue stopped.
+ Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ stop.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ stop_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} stop")
+ if verify:
+ if self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to stop {port_type} {queue_id} on port {port_id}"
+ )
+
+ def start_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Starts a given queue on a port.
+
+ First sets up the port queue, then starts it.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to start.
+ is_rx_queue: Type of queue to start. If :data:`True` an RX queue will be started,
+ otherwise a TX queue will be started.
+ verify: if :data:`True` an additional command will be sent to verify that the queue was
+ started. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ start.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ start_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} start")
+ if verify:
+ if not self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to start {port_type} {queue_id} on port {port_id}"
+ )
+
+ def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: bool) -> None:
+ """Setup a given queue on a port.
+
+ This functionality cannot be verified because the setup action only takes effect when the
+ queue is started.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to setup.
+ is_rx_queue: Type of queue to setup. If :data:`True` an RX queue will be setup,
+ otherwise a TX queue will be setup.
+ """
+ self.send_command(f"port {port_id} {'rxq' if is_rx_queue else 'txq'} {queue_id} setup")
+
+ def set_queue_ring_size(
+ self,
+ port_id: int,
+ queue_id: int,
+ size: int,
+ is_rx_queue: bool,
+ verify: bool = True,
+ ) -> None:
+ """Update the ring size of an Rx/Tx queue on a given port.
+
+ Queue is setup after setting the ring size so that the queue info reflects this change and
+ it can be verified.
+
+ Args:
+ port_id: The port that the queue resides on.
+ queue_id: The ID of the queue on the port.
+ size: The size to update the ring size to.
+ is_rx_queue: Whether to modify an RX or TX queue. If :data:`True` an RX queue will be
+ updated, otherwise a TX queue will be updated.
+ verify: If :data:`True` an additional command will be sent to check the ring size of
+ the queue in an attempt to validate that the size was changes properly.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and there is a failure
+ when updating ring size.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ self.send_command(f"port config {port_id} {queue_type} {queue_id} ring_size {size}")
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ if verify:
+ curr_ring_size = self.show_port_queue_info(port_id, queue_id, is_rx_queue).ring_size
+ if curr_ring_size != size:
+ self._logger.debug(
+ f"Failed up update ring size of queue {queue_id} on port {port_id}. Current"
+ f" ring size is {curr_ring_size}."
+ )
+ raise InteractiveCommandExecutionError(
+ f"Failed to update ring size of queue {queue_id} on port {port_id}"
+ )
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 3/4] dts: add dynamic queue test suite
2024-07-24 15:07 ` [PATCH v3 0/4] dts: add dynamic queue configuration test suite jspewock
2024-07-24 15:07 ` [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
2024-07-24 15:07 ` [PATCH v3 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
@ 2024-07-24 15:07 ` jspewock
2024-07-24 15:07 ` [PATCH v3 4/4] dts: add dynamic queue conf to the yaml schema jspewock
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-07-24 15:07 UTC (permalink / raw)
To: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
npratte, wathsala.vithanage, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds a new test suite that is designed to test the stopping
and modification of port queues at runtime. Specifically, there are
test cases that display the ports ability to stop some queues but still
send and receive traffic on others, as well as the ability to configure
the ring size of the queue without blocking the traffic on other queues.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/tests/TestSuite_dynamic_queue_conf.py | 286 ++++++++++++++++++++++
1 file changed, 286 insertions(+)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
new file mode 100644
index 0000000000..f5c667cdeb
--- /dev/null
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -0,0 +1,286 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 University of New Hampshire
+
+"""Dynamic configuration of port queues test suite.
+
+This test suite tests the support of being able to either stop or reconfigure port queues at
+runtime without stopping the entire device. Previously, to configure a DPDK ethdev, the application
+first specifies how many Tx and Rx queues to include in the ethdev and then application sets up
+each queue individually. Only once all the queues have been set up can the application then start
+the device, and at this point traffic can flow. If device stops, this halts the flow of traffic on
+all queues in the ethdev completely. Dynamic queue is a capability present on some NICs that
+specifies whether the NIC is able to delay the configuration of queues on its port. This capability
+allows for the support of stopping and reconfiguring queues on a port at runtime without stopping
+the entire device.
+
+Support of this capability is shown by starting the Poll Mode Driver with multiple Rx and Tx queues
+configured and stopping some prior to forwarding packets, then examining whether or not the stopped
+ports and the unmodified ports were able to handle traffic. In addition to just stopping the ports,
+the ports must also show that they support configuration changes on their queues at runtime without
+stopping the entire device. This is shown by changing the ring size of the queues.
+
+If the Poll Mode Driver is able to stop some queues on a port and modify them then handle traffic
+on the unmodified queues while the others are stopped, then it is the case that the device properly
+supports dynamic configuration of its queues.
+"""
+
+import random
+from typing import Callable, ClassVar, MutableSet
+
+from scapy.layers.inet import IP # type: ignore[import-untyped]
+from scapy.layers.l2 import Ether # type: ignore[import-untyped]
+from scapy.packet import Raw # type: ignore[import-untyped]
+
+from framework.exception import InteractiveCommandExecutionError
+from framework.params.testpmd import PortTopology, SimpleForwardingModes
+from framework.remote_session.testpmd_shell import TestPmdShell
+from framework.test_suite import TestSuite
+
+
+def setup_and_teardown_test(
+ test_meth: Callable[
+ ["TestDynamicQueueConf", int, MutableSet, MutableSet, TestPmdShell, bool], None
+ ],
+) -> Callable[["TestDynamicQueueConf", bool], None]:
+ """Decorator that provides a setup and teardown for testing methods.
+
+ This decorator provides a method that sets up the environment for testing, runs the test
+ method, and then does a clean-up verification step after the queues are started again. The
+ decorated method will be provided with all the variables it should need to run testing
+ including: The ID of the port where the queues for testing reside, disjoint sets of IDs for
+ queues that are/aren't modified, a testpmd session to run testing with, and a flag that
+ indicates whether or not testing should be done on Rx or Tx queues.
+
+ Args:
+ test_meth: The decorated method that tests configuration of port queues at runtime.
+ This method must have the following parameters in order: An int that represents a
+ port ID, a set of queues for testing, a set of unmodified queues, a testpmd
+ interactive shell, and a boolean that, when :data:`True`, does Rx testing,
+ otherwise does Tx testing. This method must also be a member of the
+ :class:`TestDynamicQueueConf` class.
+
+ Returns:
+ A method that sets up the environment, runs the decorated method, then re-enables all
+ queues and validates they can still handle traffic.
+ """
+
+ def wrap(self: "TestDynamicQueueConf", is_rx_testing: bool) -> None:
+ """Setup environment, run test function, then cleanup.
+
+ Start a testpmd shell and stop ports for testing, then call the decorated function that
+ performs the testing. After the decorated function is finished running its testing,
+ start the stopped queues and send packets to validate that these ports can properly
+ handle traffic after being started again.
+
+ Args:
+ self: Instance of :class:`TestDynamicQueueConf` `test_meth` belongs to.
+ is_rx_testing: If :data:`True` then Rx queues will be the ones modified throughout
+ the test, otherwise Tx queues will be modified.
+ """
+ port_id = self.rx_port_num if is_rx_testing else self.tx_port_num
+ queues_to_config: set[int] = set()
+ while len(queues_to_config) < self.num_ports_to_modify:
+ queues_to_config.add(random.randint(1, self.number_of_queues - 1))
+ unchanged_queues = set(range(self.number_of_queues)) - queues_to_config
+ with TestPmdShell(
+ self.sut_node,
+ port_topology=PortTopology.chained,
+ rx_queues=self.number_of_queues,
+ tx_queues=self.number_of_queues,
+ ) as testpmd:
+ for q in queues_to_config:
+ testpmd.stop_port_queue(port_id, q, is_rx_testing)
+ testpmd.set_forward_mode(SimpleForwardingModes.mac)
+
+ test_meth(self, port_id, queues_to_config, unchanged_queues, testpmd, is_rx_testing)
+
+ for queue_id in queues_to_config:
+ testpmd.start_port_queue(port_id, queue_id, is_rx_testing)
+
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+ for queue_id in queues_to_config:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, queue_id, forwarding_stats),
+ f"Modified queue {queue_id} on port {port_id} failed to receive traffic after"
+ "being started again.",
+ )
+
+ return wrap
+
+
+class TestDynamicQueueConf(TestSuite):
+ """DPDK dynamic queue configuration test suite.
+
+ Testing for the support of dynamic queue configuration is done by splitting testing by the type
+ of queue (either Rx or Tx) and the type of testing (testing for stopping a port at runtime vs
+ testing configuration changes at runtime). Testing is done by first stopping a finite number of
+ port queues (3 is sufficient) and either modifying the configuration or sending packets to
+ verify that the unmodified queues can handle traffic. Specifically, the following cases are
+ tested:
+
+ 1. The application should be able to start the device with only some of the
+ queues set up.
+ 2. The application should be able to reconfigure existing queues at runtime
+ without calling dev_stop().
+ """
+
+ #:
+ num_ports_to_modify: ClassVar[int] = 3
+ #: Source IP address to use when sending packets.
+ src_addr: ClassVar[str] = "192.168.0.1"
+ #: Subnet to use for all of the destination addresses of the packets being sent.
+ dst_address_subnet: ClassVar[str] = "192.168.1"
+ #: ID of the port to modify Rx queues on.
+ rx_port_num: ClassVar[int] = 0
+ #: ID of the port to modify Tx queues on.
+ tx_port_num: ClassVar[int] = 1
+ #: Number of queues to start testpmd with. There will be the same number of Rx and Tx queues.
+ #: 8 was chosen as a number that is low enough for most NICs to accommodate while also being
+ #: enough to validate the usage of the queues.
+ number_of_queues: ClassVar[int] = 8
+ #: The number of packets to send while testing. The test calls for well over the ring size - 1
+ #: packets in the modification test case and the only options for ring size are 256 or 512,
+ #: therefore 1024 will be more than enough.
+ number_of_packets_to_send: ClassVar[int] = 1024
+
+ def send_packets_with_different_addresses(self, number_of_packets: int) -> None:
+ """Send a set number of packets each with different dst addresses.
+
+ Different destination addresses are required to ensure that each queue is used. If every
+ packet had the same address, then they would all be processed by the same queue. Note that
+ this means the current implementation of this method is limited to only work for up to 254
+ queues. A smaller subnet would be required to handle an increased number of queues.
+
+ Args:
+ number_of_packets: The number of packets to generate and then send using the traffic
+ generator.
+ """
+ packets_to_send = [
+ Ether()
+ / IP(src=self.src_addr, dst=f"{self.dst_address_subnet}.{(i % 254) + 1}")
+ / Raw()
+ for i in range(number_of_packets)
+ ]
+ self.send_packets(packets_to_send)
+
+ def port_queue_in_stats(
+ self, port_id: int, is_rx_queue: bool, queue_id: int, stats: str
+ ) -> bool:
+ """Verify if stats for a queue are in the provided output.
+
+ Args:
+ port_id: ID of the port that the queue resides on.
+ is_rx_queue: Type of queue to scan for, if :data:`True` then search for an Rx queue,
+ otherwise search for a Tx queue.
+ queue_id: ID of the queue.
+ stats: Testpmd forwarding statistics to scan for the given queue.
+
+ Returns:
+ If the queue appeared in the forwarding statistics.
+ """
+ type_of_queue = "RX" if is_rx_queue else "TX"
+ return f"{type_of_queue} Port= {port_id}/Queue={queue_id:2d}" in stats
+
+ @setup_and_teardown_test
+ def modify_ring_size(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify ring size of port queues can be configured at runtime.
+
+ Ring size of queues in `queues_to_modify` are set to 512 unless that is already their
+ configured size, in which case they are instead set to 256. Queues in `queues_to_modify`
+ are expected to already be stopped before calling this method. `testpmd` is also expected
+ to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ for queue_id in queues_to_modify:
+ curr_ring_size = testpmd.show_port_queue_info(
+ port_id, queue_id, is_rx_testing
+ ).ring_size
+ new_ring_size = 256 if curr_ring_size == 512 else 512
+ try:
+ testpmd.set_queue_ring_size(
+ port_id, queue_id, new_ring_size, is_rx_testing, verify=True
+ )
+ # The testpmd method verifies that the modification worked, so we catch that error
+ # and just re-raise it as a test case failure
+ except InteractiveCommandExecutionError:
+ self.verify(
+ False,
+ f"Failed to update the ring size of queue {queue_id} on port "
+ f"{port_id} at runtime",
+ )
+
+ @setup_and_teardown_test
+ def stop_queues(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify stopped queues do not handle traffic and do not block traffic on other queues.
+
+ Queues in `queues_to_modify` are expected to already be stopped before calling this method.
+ `testpmd` is also expected to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+
+ # Checking that all unmodified queues handled some packets is important because this
+ # test case checks for the absence of stopped queues to validate that they cannot
+ # receive traffic. If there are some unchanged queues that also didn't receive traffic,
+ # it means there could be another reason for the packets not transmitting and,
+ # therefore, a false positive result.
+ for unchanged_q_id in unchanged_queues:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, unchanged_q_id, forwarding_stats),
+ f"Queue {unchanged_q_id} failed to receive traffic.",
+ )
+ for stopped_q_id in queues_to_modify:
+ self.verify(
+ not self.port_queue_in_stats(
+ port_id, is_rx_testing, stopped_q_id, forwarding_stats
+ ),
+ f"Queue {stopped_q_id} should be stopped but still received traffic.",
+ )
+
+ def test_rx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`True`."""
+ self.stop_queues(True)
+
+ def test_rx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`True`."""
+ self.modify_ring_size(True)
+
+ def test_tx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`False`."""
+ self.stop_queues(False)
+
+ def test_tx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`False`."""
+ self.modify_ring_size(False)
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 4/4] dts: add dynamic queue conf to the yaml schema
2024-07-24 15:07 ` [PATCH v3 0/4] dts: add dynamic queue configuration test suite jspewock
` (2 preceding siblings ...)
2024-07-24 15:07 ` [PATCH v3 3/4] dts: add dynamic queue test suite jspewock
@ 2024-07-24 15:07 ` jspewock
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-07-24 15:07 UTC (permalink / raw)
To: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
npratte, wathsala.vithanage, juraj.linkes, paul.szczepanek
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
Adds the ability to run the test suite using the yaml configuration
file.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/config/conf_yaml_schema.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index f02a310bb5..d83a2f51c5 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -187,7 +187,8 @@
"enum": [
"hello_world",
"os_udp",
- "pmd_buffer_scatter"
+ "pmd_buffer_scatter",
+ "dynamic_queue_conf"
]
},
"test_target": {
--
2.45.2
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing
2024-07-24 15:07 ` [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
@ 2024-07-26 14:37 ` Nicholas Pratte
2024-07-26 19:00 ` Nicholas Pratte
1 sibling, 0 replies; 81+ messages in thread
From: Nicholas Pratte @ 2024-07-26 14:37 UTC (permalink / raw)
To: jspewock
Cc: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
wathsala.vithanage, juraj.linkes, paul.szczepanek, dev
This is great, I'll be using this in-favor of the boolean solution
that I implemented! Just to bring this to your attention. I am
currently working on some Generic Routing Encapsulation suites the
require multiple IP layers at packet creation; they look something
like:
Ether() / IP() / GRE() / IP() / UDP() / Raw(load='x'*80)
I have to take a deeper look to see how multiple IP layers affect the
declaration of src and dst variables, I'll let you know what I find as
some changes might be needed on this implementation to avoid future
bugs. Once I figure it out, I'll leave a review tag for you.
On Wed, Jul 24, 2024 at 11:07 AM <jspewock@iol.unh.edu> wrote:
>
> From: Jeremy Spewock <jspewock@iol.unh.edu>
>
> Currently the only method provided in the test suite class for sending
> packets sends a single packet and then captures the results. There is,
> in some cases, a need to send multiple packets at once while not really
> needing to capture any traffic received back. The method to do this
> exists in the traffic generator already, but this patch exposes the
> method to test suites.
>
> This patch also updates the _adjust_addresses method of test suites so
> that addresses of packets are only modified if the developer did not
> configure them beforehand. This allows for developers to have more
> control over the content of their packets when sending them through the
> framework.
>
> Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
> ---
> dts/framework/test_suite.py | 74 ++++++++++++++++++--------
> dts/framework/testbed_model/tg_node.py | 9 ++++
> 2 files changed, 62 insertions(+), 21 deletions(-)
>
> diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
> index 694b2eba65..0b678ed62d 100644
> --- a/dts/framework/test_suite.py
> +++ b/dts/framework/test_suite.py
> @@ -199,7 +199,7 @@ def send_packet_and_capture(
> Returns:
> A list of received packets.
> """
> - packet = self._adjust_addresses(packet)
> + packet = self._adjust_addresses([packet])[0]
> return self.tg_node.send_packet_and_capture(
> packet,
> self._tg_port_egress,
> @@ -208,6 +208,18 @@ def send_packet_and_capture(
> duration,
> )
>
> + def send_packets(
> + self,
> + packets: list[Packet],
> + ) -> None:
> + """Send packets using the traffic generator and do not capture received traffic.
> +
> + Args:
> + packets: Packets to send.
> + """
> + packets = self._adjust_addresses(packets)
> + self.tg_node.send_packets(packets, self._tg_port_egress)
> +
> def get_expected_packet(self, packet: Packet) -> Packet:
> """Inject the proper L2/L3 addresses into `packet`.
>
> @@ -219,39 +231,59 @@ def get_expected_packet(self, packet: Packet) -> Packet:
> """
> return self._adjust_addresses(packet, expected=True)
>
> - def _adjust_addresses(self, packet: Packet, expected: bool = False) -> Packet:
> + def _adjust_addresses(self, packets: list[Packet], expected: bool = False) -> list[Packet]:
> """L2 and L3 address additions in both directions.
>
> + Only missing addresses are added to packets, existing addressed will not be overridden.
> +
> Assumptions:
> Two links between SUT and TG, one link is TG -> SUT, the other SUT -> TG.
>
> Args:
> - packet: The packet to modify.
> + packets: The packets to modify.
> expected: If :data:`True`, the direction is SUT -> TG,
> otherwise the direction is TG -> SUT.
> """
> - if expected:
> - # The packet enters the TG from SUT
> - # update l2 addresses
> - packet.src = self._sut_port_egress.mac_address
> - packet.dst = self._tg_port_ingress.mac_address
> + ret_packets = []
> + for packet in packets:
> + default_pkt_src = type(packet)().src
> + default_pkt_dst = type(packet)().dst
> + default_pkt_payload_src = IP().src if hasattr(packet.payload, "src") else None
> + default_pkt_payload_dst = IP().dst if hasattr(packet.payload, "dst") else None
> + # If `expected` is :data:`True`, the packet enters the TG from SUT, otherwise the
> + # packet leaves the TG towards the SUT
>
> - # The packet is routed from TG egress to TG ingress
> - # update l3 addresses
> - packet.payload.src = self._tg_ip_address_egress.ip.exploded
> - packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
> - else:
> - # The packet leaves TG towards SUT
> # update l2 addresses
> - packet.src = self._tg_port_egress.mac_address
> - packet.dst = self._sut_port_ingress.mac_address
> + if packet.src == default_pkt_src:
> + packet.src = (
> + self._sut_port_egress.mac_address
> + if expected
> + else self._tg_port_egress.mac_address
> + )
> + if packet.dst == default_pkt_dst:
> + packet.dst = (
> + self._tg_port_ingress.mac_address
> + if expected
> + else self._sut_port_ingress.mac_address
> + )
> +
> + # The packet is routed from TG egress to TG ingress regardless of if it is expected or
> + # not.
>
> - # The packet is routed from TG egress to TG ingress
> # update l3 addresses
> - packet.payload.src = self._tg_ip_address_egress.ip.exploded
> - packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
> -
> - return Ether(packet.build())
> + if (
> + default_pkt_payload_src is not None
> + and packet.payload.src == default_pkt_payload_src
> + ):
> + packet.payload.src = self._tg_ip_address_egress.ip.exploded
> + if (
> + default_pkt_payload_dst is not None
> + and packet.payload.dst == default_pkt_payload_dst
> + ):
> + packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
> + ret_packets.append(Ether(packet.build()))
> +
> + return ret_packets
>
> def verify(self, condition: bool, failure_description: str) -> None:
> """Verify `condition` and handle failures.
> diff --git a/dts/framework/testbed_model/tg_node.py b/dts/framework/testbed_model/tg_node.py
> index 4ee326e99c..758b676258 100644
> --- a/dts/framework/testbed_model/tg_node.py
> +++ b/dts/framework/testbed_model/tg_node.py
> @@ -83,6 +83,15 @@ def send_packet_and_capture(
> duration,
> )
>
> + def send_packets(self, packets: list[Packet], port: Port):
> + """Send packets without capturing resulting received packets.
> +
> + Args:
> + packets: Packets to send.
> + port: Port to send the packets on.
> + """
> + self.traffic_generator.send_packets(packets, port)
> +
> def close(self) -> None:
> """Free all resources used by the node.
>
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing
2024-07-24 15:07 ` [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
2024-07-26 14:37 ` Nicholas Pratte
@ 2024-07-26 19:00 ` Nicholas Pratte
2024-07-26 19:13 ` Jeremy Spewock
1 sibling, 1 reply; 81+ messages in thread
From: Nicholas Pratte @ 2024-07-26 19:00 UTC (permalink / raw)
To: jspewock
Cc: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
wathsala.vithanage, juraj.linkes, paul.szczepanek, dev
I'll make sure to look over the other parts of this series and leave
reviews at some point next week, but I prioritized this since I will
be using this patch at some point in my GRE suites.
> diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
> index 694b2eba65..0b678ed62d 100644
> --- a/dts/framework/test_suite.py
> +++ b/dts/framework/test_suite.py
> @@ -199,7 +199,7 @@ def send_packet_and_capture(
> Returns:
> A list of received packets.
> """
> - packet = self._adjust_addresses(packet)
> + packet = self._adjust_addresses([packet])[0]
> return self.tg_node.send_packet_and_capture(
> packet,
> self._tg_port_egress,
> @@ -208,6 +208,18 @@ def send_packet_and_capture(
> duration,
> )
>
> + def send_packets(
> + self,
> + packets: list[Packet],
> + ) -> None:
> + """Send packets using the traffic generator and do not capture received traffic.
> +
> + Args:
> + packets: Packets to send.
> + """
> + packets = self._adjust_addresses(packets)
> + self.tg_node.send_packets(packets, self._tg_port_egress)
> +
> def get_expected_packet(self, packet: Packet) -> Packet:
> """Inject the proper L2/L3 addresses into `packet`.
>
> @@ -219,39 +231,59 @@ def get_expected_packet(self, packet: Packet) -> Packet:
> """
> return self._adjust_addresses(packet, expected=True)
>
> - def _adjust_addresses(self, packet: Packet, expected: bool = False) -> Packet:
> + def _adjust_addresses(self, packets: list[Packet], expected: bool = False) -> list[Packet]:
> """L2 and L3 address additions in both directions.
>
> + Only missing addresses are added to packets, existing addressed will not be overridden.
addressed should be addresses. Only saw this because of Chrome's
built-in grammar correction.
> +
> Assumptions:
> Two links between SUT and TG, one link is TG -> SUT, the other SUT -> TG.
>
> Args:
> - packet: The packet to modify.
> + packets: The packets to modify.
> expected: If :data:`True`, the direction is SUT -> TG,
> otherwise the direction is TG -> SUT.
> """
> - if expected:
> - # The packet enters the TG from SUT
> - # update l2 addresses
> - packet.src = self._sut_port_egress.mac_address
> - packet.dst = self._tg_port_ingress.mac_address
> + ret_packets = []
> + for packet in packets:
> + default_pkt_src = type(packet)().src
> + default_pkt_dst = type(packet)().dst
This is really just a probing question for my sake, but what is the
difference between the solution you have above type(packet)().src and
Ether().src? Is there a preferred means of doing this?
> + default_pkt_payload_src = IP().src if hasattr(packet.payload, "src") else None
> + default_pkt_payload_dst = IP().dst if hasattr(packet.payload, "dst") else None
> + # If `expected` is :data:`True`, the packet enters the TG from SUT, otherwise the
> + # packet leaves the TG towards the SUT
>
> - # The packet is routed from TG egress to TG ingress
> - # update l3 addresses
> - packet.payload.src = self._tg_ip_address_egress.ip.exploded
> - packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
This is where it gets a little tricky. There will be circumstances,
albeit probably infrequently, where a user-created packet has more
than one IP layer, such as the ones I am using in the ipgre and nvgre
test suites that I am writing. In these cases, you need to specify an
index of the IP layer you want to modify, otherwise it will modify the
outermost IP layer in the packet (the IP layer outside the GRE layer.
See my previous comment for an example packet). Should be pretty easy
to fix, you just need to check if a packet contains an GRE layer, and
if it does, modify the packet by doing something like
packet[IP][1].src = self._tg_ip_address_egress.ip.exploded.
> - else:
> - # The packet leaves TG towards SUT
> # update l2 addresses
> - packet.src = self._tg_port_egress.mac_address
> - packet.dst = self._sut_port_ingress.mac_address
You wouldn't need to make changes to how Ether addresses get allocated
if accounting for GRE as described above, since I'm pretty sure there
aren't really circumstances where packets would have more than one
Ethernet header, at least not that I've seen (GRE packets only have
one).
> + if packet.src == default_pkt_src:
> + packet.src = (
> + self._sut_port_egress.mac_address
> + if expected
> + else self._tg_port_egress.mac_address
> + )
> + if packet.dst == default_pkt_dst:
> + packet.dst = (
> + self._tg_port_ingress.mac_address
> + if expected
> + else self._sut_port_ingress.mac_address
> + )
> +
> + # The packet is routed from TG egress to TG ingress regardless of if it is expected or
Maybe change 'regardless of if it is expected' to 'regardless of
whether it is expected.' It's admittedly picky of me, but I think it
reads a little bit better.
> + # not.
>
> - # The packet is routed from TG egress to TG ingress
> # update l3 addresses
> - packet.payload.src = self._tg_ip_address_egress.ip.exploded
> - packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
> -
> - return Ether(packet.build())
> + if (
> + default_pkt_payload_src is not None
> + and packet.payload.src == default_pkt_payload_src
> + ):
> + packet.payload.src = self._tg_ip_address_egress.ip.exploded
> + if (
> + default_pkt_payload_dst is not None
> + and packet.payload.dst == default_pkt_payload_dst
> + ):
> + packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
> + ret_packets.append(Ether(packet.build()))
> +
> + return ret_packets
>
> def verify(self, condition: bool, failure_description: str) -> None:
> """Verify `condition` and handle failures.
> diff --git a/dts/framework/testbed_model/tg_node.py b/dts/framework/testbed_model/tg_node.py
> index 4ee326e99c..758b676258 100644
> --- a/dts/framework/testbed_model/tg_node.py
> +++ b/dts/framework/testbed_model/tg_node.py
> @@ -83,6 +83,15 @@ def send_packet_and_capture(
> duration,
> )
>
> + def send_packets(self, packets: list[Packet], port: Port):
> + """Send packets without capturing resulting received packets.
> +
> + Args:
> + packets: Packets to send.
> + port: Port to send the packets on.
> + """
> + self.traffic_generator.send_packets(packets, port)
> +
> def close(self) -> None:
> """Free all resources used by the node.
>
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing
2024-07-26 19:00 ` Nicholas Pratte
@ 2024-07-26 19:13 ` Jeremy Spewock
2024-08-29 19:42 ` Nicholas Pratte
0 siblings, 1 reply; 81+ messages in thread
From: Jeremy Spewock @ 2024-07-26 19:13 UTC (permalink / raw)
To: Nicholas Pratte
Cc: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
wathsala.vithanage, juraj.linkes, paul.szczepanek, dev
Thanks for the comments, I just had one clarifying question about
them, but otherwise I will address them in the next version.
On Fri, Jul 26, 2024 at 3:00 PM Nicholas Pratte <npratte@iol.unh.edu> wrote:
>
> I'll make sure to look over the other parts of this series and leave
> reviews at some point next week, but I prioritized this since I will
> be using this patch at some point in my GRE suites.
>
>
> > diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
> > index 694b2eba65..0b678ed62d 100644
> > --- a/dts/framework/test_suite.py
> > +++ b/dts/framework/test_suite.py
> > @@ -199,7 +199,7 @@ def send_packet_and_capture(
> > Returns:
> > A list of received packets.
> > """
> > - packet = self._adjust_addresses(packet)
> > + packet = self._adjust_addresses([packet])[0]
> > return self.tg_node.send_packet_and_capture(
> > packet,
> > self._tg_port_egress,
> > @@ -208,6 +208,18 @@ def send_packet_and_capture(
> > duration,
> > )
> >
> > + def send_packets(
> > + self,
> > + packets: list[Packet],
> > + ) -> None:
> > + """Send packets using the traffic generator and do not capture received traffic.
> > +
> > + Args:
> > + packets: Packets to send.
> > + """
> > + packets = self._adjust_addresses(packets)
> > + self.tg_node.send_packets(packets, self._tg_port_egress)
> > +
> > def get_expected_packet(self, packet: Packet) -> Packet:
> > """Inject the proper L2/L3 addresses into `packet`.
> >
> > @@ -219,39 +231,59 @@ def get_expected_packet(self, packet: Packet) -> Packet:
> > """
> > return self._adjust_addresses(packet, expected=True)
> >
> > - def _adjust_addresses(self, packet: Packet, expected: bool = False) -> Packet:
> > + def _adjust_addresses(self, packets: list[Packet], expected: bool = False) -> list[Packet]:
> > """L2 and L3 address additions in both directions.
> >
> > + Only missing addresses are added to packets, existing addressed will not be overridden.
>
> addressed should be addresses. Only saw this because of Chrome's
> built-in grammar correction.
Good catch.
>
> > +
> > Assumptions:
> > Two links between SUT and TG, one link is TG -> SUT, the other SUT -> TG.
> >
> > Args:
> > - packet: The packet to modify.
> > + packets: The packets to modify.
> > expected: If :data:`True`, the direction is SUT -> TG,
> > otherwise the direction is TG -> SUT.
> > """
> > - if expected:
> > - # The packet enters the TG from SUT
> > - # update l2 addresses
> > - packet.src = self._sut_port_egress.mac_address
> > - packet.dst = self._tg_port_ingress.mac_address
> > + ret_packets = []
> > + for packet in packets:
> > + default_pkt_src = type(packet)().src
> > + default_pkt_dst = type(packet)().dst
>
> This is really just a probing question for my sake, but what is the
> difference between the solution you have above type(packet)().src and
> Ether().src? Is there a preferred means of doing this?
There isn't really a functional difference at all under the assumption
that every packet we send will start with an Ethernet header. This
obviously isn't an unreasonable assumption to make, so maybe I was
reaching for flexibility that isn't really needed here by making it
work with any theoretical first layer that has a source address. I
wanted to do the same thing for the payload, but that causes issues
when the following layer with an address isn't the very next layer
after Ether.
>
> > + default_pkt_payload_src = IP().src if hasattr(packet.payload, "src") else None
> > + default_pkt_payload_dst = IP().dst if hasattr(packet.payload, "dst") else None
> > + # If `expected` is :data:`True`, the packet enters the TG from SUT, otherwise the
> > + # packet leaves the TG towards the SUT
> >
> > - # The packet is routed from TG egress to TG ingress
> > - # update l3 addresses
> > - packet.payload.src = self._tg_ip_address_egress.ip.exploded
> > - packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
>
> This is where it gets a little tricky. There will be circumstances,
> albeit probably infrequently, where a user-created packet has more
> than one IP layer, such as the ones I am using in the ipgre and nvgre
> test suites that I am writing. In these cases, you need to specify an
> index of the IP layer you want to modify, otherwise it will modify the
> outermost IP layer in the packet (the IP layer outside the GRE layer.
> See my previous comment for an example packet). Should be pretty easy
> to fix, you just need to check if a packet contains an GRE layer, and
> if it does, modify the packet by doing something like
> packet[IP][1].src = self._tg_ip_address_egress.ip.exploded.
I'm not as familiar with how GRE affects the packets, do you need to
have the address on the inner IP layer at all times, or are you saying
you need it on both IP layers?
>
> > - else:
> > - # The packet leaves TG towards SUT
> > # update l2 addresses
> > - packet.src = self._tg_port_egress.mac_address
> > - packet.dst = self._sut_port_ingress.mac_address
>
> You wouldn't need to make changes to how Ether addresses get allocated
> if accounting for GRE as described above, since I'm pretty sure there
> aren't really circumstances where packets would have more than one
> Ethernet header, at least not that I've seen (GRE packets only have
> one).
>
> > + if packet.src == default_pkt_src:
> > + packet.src = (
> > + self._sut_port_egress.mac_address
> > + if expected
> > + else self._tg_port_egress.mac_address
> > + )
> > + if packet.dst == default_pkt_dst:
> > + packet.dst = (
> > + self._tg_port_ingress.mac_address
> > + if expected
> > + else self._sut_port_ingress.mac_address
> > + )
> > +
> > + # The packet is routed from TG egress to TG ingress regardless of if it is expected or
>
> Maybe change 'regardless of if it is expected' to 'regardless of
> whether it is expected.' It's admittedly picky of me, but I think it
> reads a little bit better.
Ack.
>
> > + # not.
> >
> > - # The packet is routed from TG egress to TG ingress
> > # update l3 addresses
> > - packet.payload.src = self._tg_ip_address_egress.ip.exploded
> > - packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
<snip>
> >
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing
2024-07-26 19:13 ` Jeremy Spewock
@ 2024-08-29 19:42 ` Nicholas Pratte
0 siblings, 0 replies; 81+ messages in thread
From: Nicholas Pratte @ 2024-08-29 19:42 UTC (permalink / raw)
To: Jeremy Spewock
Cc: thomas, Luca.Vizzarro, Honnappa.Nagarahalli, probb, yoan.picchi,
wathsala.vithanage, juraj.linkes, paul.szczepanek, dev
Hi Jeremy, sorry for the delay! See my comments below.
<snip>
> > > Assumptions:
> > > Two links between SUT and TG, one link is TG -> SUT, the other SUT -> TG.
> > >
> > > Args:
> > > - packet: The packet to modify.
> > > + packets: The packets to modify.
> > > expected: If :data:`True`, the direction is SUT -> TG,
> > > otherwise the direction is TG -> SUT.
> > > """
> > > - if expected:
> > > - # The packet enters the TG from SUT
> > > - # update l2 addresses
> > > - packet.src = self._sut_port_egress.mac_address
> > > - packet.dst = self._tg_port_ingress.mac_address
> > > + ret_packets = []
> > > + for packet in packets:
> > > + default_pkt_src = type(packet)().src
> > > + default_pkt_dst = type(packet)().dst
> >
> > This is really just a probing question for my sake, but what is the
> > difference between the solution you have above type(packet)().src and
> > Ether().src? Is there a preferred means of doing this?
>
> There isn't really a functional difference at all under the assumption
> that every packet we send will start with an Ethernet header. This
> obviously isn't an unreasonable assumption to make, so maybe I was
> reaching for flexibility that isn't really needed here by making it
> work with any theoretical first layer that has a source address. I
> wanted to do the same thing for the payload, but that causes issues
> when the following layer with an address isn't the very next layer
> after Ether.
Makes sense to me! It's probably best to not to make the Ether
assumption regardless of whether or not it will likely always be
present.
>
> >
> > > + default_pkt_payload_src = IP().src if hasattr(packet.payload, "src") else None
> > > + default_pkt_payload_dst = IP().dst if hasattr(packet.payload, "dst") else None
> > > + # If `expected` is :data:`True`, the packet enters the TG from SUT, otherwise the
> > > + # packet leaves the TG towards the SUT
> > >
> > > - # The packet is routed from TG egress to TG ingress
> > > - # update l3 addresses
> > > - packet.payload.src = self._tg_ip_address_egress.ip.exploded
> > > - packet.payload.dst = self._tg_ip_address_ingress.ip.exploded
> >
> > This is where it gets a little tricky. There will be circumstances,
> > albeit probably infrequently, where a user-created packet has more
> > than one IP layer, such as the ones I am using in the ipgre and nvgre
> > test suites that I am writing. In these cases, you need to specify an
> > index of the IP layer you want to modify, otherwise it will modify the
> > outermost IP layer in the packet (the IP layer outside the GRE layer.
> > See my previous comment for an example packet). Should be pretty easy
> > to fix, you just need to check if a packet contains an GRE layer, and
> > if it does, modify the packet by doing something like
> > packet[IP][1].src = self._tg_ip_address_egress.ip.exploded.
>
> I'm not as familiar with how GRE affects the packets, do you need to
> have the address on the inner IP layer at all times, or are you saying
> you need it on both IP layers?
Basically, GRE is a header that encapsulates a traditional packet.
Practically speaking, this means that a scapy packet with GRE will
look something like 'Ether() / IP() / GRE() / IP() / UDP() / Raw()'.
If you try to modify layer 3 addresses in the way the framework does
it now (packet.payload.src), and more than one IP layer is present in
a given packet, it will modify the the front-most IP layer (in this
case, the IP layer before the GRE layer is the packet I listed
before). If there are multiple IP layers, you can choose which layer
you want to modify by doing something like 'packet[IP][1] = address'
to modify the inner IP layer.
It is my understanding that GRE packets need to have an inner IP layer
as well as an outer IP layer. Here is a quick readup on what GRE is
(scroll to the bottom of the article and look at the diagram of a
regular datagram vs a GRE datagram as the rest of the article isn't
super important).
https://ipwithease.com/generic-routing-encapsulation-gre/
<snip>
-Nicholas
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v4 0/2] dts: add dynamic queue configuration test suite
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
` (5 preceding siblings ...)
2024-07-24 15:07 ` [PATCH v3 0/4] dts: add dynamic queue configuration test suite jspewock
@ 2024-09-04 15:49 ` jspewock
2024-09-04 15:49 ` [PATCH v4 1/2] dts: add port queue modification and forwarding stats to testpmd jspewock
2024-09-04 15:49 ` [PATCH v4 2/2] dts: add dynamic queue test suite jspewock
2024-09-25 19:20 ` [PATCH v5 0/2] dts: add dynamic queue configuration " jspewock
7 siblings, 2 replies; 81+ messages in thread
From: jspewock @ 2024-09-04 15:49 UTC (permalink / raw)
To: wathsala.vithanage, probb, Luca.Vizzarro, alex.chapman,
juraj.linkes, thomas, paul.szczepanek, yoan.picchi, npratte,
Honnappa.Nagarahalli
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v4:
* split patch for updating packet addressing into its own series and
added a dependency in this one.
* squash commit for adding test suite to the yaml schema into the last
commit.
Jeremy Spewock (2):
dts: add port queue modification and forwarding stats to testpmd
dts: add dynamic queue test suite
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/framework/remote_session/testpmd_shell.py | 233 +++++++++++++-
dts/tests/TestSuite_dynamic_queue_conf.py | 286 ++++++++++++++++++
3 files changed, 519 insertions(+), 3 deletions(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
--
2.46.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v4 1/2] dts: add port queue modification and forwarding stats to testpmd
2024-09-04 15:49 ` [PATCH v4 0/2] dts: add dynamic queue configuration test suite jspewock
@ 2024-09-04 15:49 ` jspewock
2024-09-04 15:49 ` [PATCH v4 2/2] dts: add dynamic queue test suite jspewock
1 sibling, 0 replies; 81+ messages in thread
From: jspewock @ 2024-09-04 15:49 UTC (permalink / raw)
To: wathsala.vithanage, probb, Luca.Vizzarro, alex.chapman,
juraj.linkes, thomas, paul.szczepanek, yoan.picchi, npratte,
Honnappa.Nagarahalli
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds methods for querying and modifying port queue state and
configuration. In addition to this, it also adds the ability to capture
the forwarding statistics that get outputted when you send the "stop"
command in testpmd. Querying of port queue information is handled
through a TextParser dataclass in case there is future need for using
more of the output from the command used to query the information.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 233 +++++++++++++++++-
1 file changed, 231 insertions(+), 2 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 43e9f56517..b545040638 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -19,7 +19,7 @@
from dataclasses import dataclass, field
from enum import Flag, auto
from pathlib import PurePath
-from typing import ClassVar
+from typing import ClassVar, cast
from typing_extensions import Self, Unpack
@@ -541,6 +541,56 @@ class TestPmdPort(TextParser):
)
+@dataclass
+class TestPmdPortQueue(TextParser):
+ """Dataclass representation of the common parts of the testpmd `show rxq/txq info` commands."""
+
+ #:
+ prefetch_threshold: int = field(metadata=TextParser.find_int(r"prefetch threshold: (\d+)"))
+ #:
+ host_threshold: int = field(metadata=TextParser.find_int(r"host threshold: (\d+)"))
+ #:
+ writeback_threshold: int = field(metadata=TextParser.find_int(r"writeback threshold: (\d+)"))
+ #:
+ free_threshold: int = field(metadata=TextParser.find_int(r"free threshold: (\d+)"))
+ #:
+ deferred_start: bool = field(metadata=TextParser.find("deferred start: on"))
+ #: The number of RXD/TXDs is just the ring size of the queue.
+ ring_size: int = field(metadata=TextParser.find_int(r"Number of (?:RXDs|TXDs): (\d+)"))
+ #:
+ is_queue_started: bool = field(metadata=TextParser.find("queue state: started"))
+ #:
+ burst_mode: str | None = field(
+ default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)")
+ )
+
+
+@dataclass
+class TestPmdTxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show txq info` command."""
+
+ #:
+ rs_threshold: int | None = field(
+ default=None, metadata=TextParser.find_int(r"RS threshold: (\d+)")
+ )
+
+
+@dataclass
+class TestPmdRxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show rxq info` command."""
+
+ #:
+ mempool: str | None = field(default=None, metadata=TextParser.find(r"Mempool: ([^\r\n]+)"))
+ #:
+ can_drop_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"drop packets: on")
+ )
+ #:
+ is_scattering_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"scattered packets: on")
+ )
+
+
@dataclass
class TestPmdPortStats(TextParser):
"""Port statistics."""
@@ -645,7 +695,7 @@ def start(self, verify: bool = True) -> None:
"Not all ports came up after starting packet forwarding in testpmd."
)
- def stop(self, verify: bool = True) -> None:
+ def stop(self, verify: bool = True) -> str:
"""Stop packet forwarding.
Args:
@@ -653,6 +703,9 @@ def stop(self, verify: bool = True) -> None:
forwarding was stopped successfully or not started. If neither is found, it is
considered an error.
+ Returns:
+ Output gathered from sending the stop command.
+
Raises:
InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
forwarding results in an error.
@@ -665,6 +718,7 @@ def stop(self, verify: bool = True) -> None:
):
self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}")
raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.")
+ return stop_cmd_output
def get_devices(self) -> list[TestPmdDevice]:
"""Get a list of device names that are known to testpmd.
@@ -806,6 +860,181 @@ def show_port_stats(self, port_id: int) -> TestPmdPortStats:
return TestPmdPortStats.parse(output)
+ def show_port_queue_info(
+ self, port_id: int, queue_id: int, is_rx_queue: bool
+ ) -> TestPmdPortQueue:
+ """Get the info for a queue on a given port.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+ is_rx_queue: Whether to check an RX or TX queue. If :data:`True` an RX queue will be
+ queried, otherwise a TX queue will be queried.
+
+ Raises:
+ InteractiveCommandExecutionError: If there is a failure when getting the info for the
+ queue.
+
+ Returns:
+ Information about the queue on the given port.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ queue_info = self.send_command(
+ f"show {queue_type} info {port_id} {queue_id}", skip_first_line=True
+ )
+ if queue_info.startswith("ETHDEV: Invalid"):
+ raise InteractiveCommandExecutionError(
+ f"Could not get the info for {queue_type} {queue_id} on port {port_id}"
+ )
+ return (
+ TestPmdRxPortQueue.parse(queue_info)
+ if is_rx_queue
+ else TestPmdTxPortQueue.parse(queue_info)
+ )
+
+ def show_port_rx_queue_info(self, port_id: int, queue_id: int) -> TestPmdRxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdRxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Rx queue on the given port.
+ """
+ return cast(TestPmdRxPortQueue, self.show_port_queue_info(port_id, queue_id, True))
+
+ def show_port_tx_queue_info(self, port_id: int, queue_id: int) -> TestPmdTxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdTxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Tx queue on the given port.
+ """
+ return cast(TestPmdTxPortQueue, self.show_port_queue_info(port_id, queue_id, False))
+
+ def stop_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Stops a given queue on a port.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to stop.
+ is_rx_queue: Type of queue to stop. If :data:`True` an RX queue will be stopped,
+ otherwise a TX queue will be stopped.
+ verify: If :data:`True` an additional command will be sent to verify the queue stopped.
+ Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ stop.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ stop_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} stop")
+ if verify:
+ if self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to stop {port_type} {queue_id} on port {port_id}"
+ )
+
+ def start_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Starts a given queue on a port.
+
+ First sets up the port queue, then starts it.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to start.
+ is_rx_queue: Type of queue to start. If :data:`True` an RX queue will be started,
+ otherwise a TX queue will be started.
+ verify: if :data:`True` an additional command will be sent to verify that the queue was
+ started. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ start.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ start_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} start")
+ if verify:
+ if not self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to start {port_type} {queue_id} on port {port_id}"
+ )
+
+ def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: bool) -> None:
+ """Setup a given queue on a port.
+
+ This functionality cannot be verified because the setup action only takes effect when the
+ queue is started.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to setup.
+ is_rx_queue: Type of queue to setup. If :data:`True` an RX queue will be setup,
+ otherwise a TX queue will be setup.
+ """
+ self.send_command(f"port {port_id} {'rxq' if is_rx_queue else 'txq'} {queue_id} setup")
+
+ def set_queue_ring_size(
+ self,
+ port_id: int,
+ queue_id: int,
+ size: int,
+ is_rx_queue: bool,
+ verify: bool = True,
+ ) -> None:
+ """Update the ring size of an Rx/Tx queue on a given port.
+
+ Queue is setup after setting the ring size so that the queue info reflects this change and
+ it can be verified.
+
+ Args:
+ port_id: The port that the queue resides on.
+ queue_id: The ID of the queue on the port.
+ size: The size to update the ring size to.
+ is_rx_queue: Whether to modify an RX or TX queue. If :data:`True` an RX queue will be
+ updated, otherwise a TX queue will be updated.
+ verify: If :data:`True` an additional command will be sent to check the ring size of
+ the queue in an attempt to validate that the size was changes properly.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and there is a failure
+ when updating ring size.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ self.send_command(f"port config {port_id} {queue_type} {queue_id} ring_size {size}")
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ if verify:
+ curr_ring_size = self.show_port_queue_info(port_id, queue_id, is_rx_queue).ring_size
+ if curr_ring_size != size:
+ self._logger.debug(
+ f"Failed up update ring size of queue {queue_id} on port {port_id}. Current"
+ f" ring size is {curr_ring_size}."
+ )
+ raise InteractiveCommandExecutionError(
+ f"Failed to update ring size of queue {queue_id} on port {port_id}"
+ )
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
--
2.46.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 2/2] dts: add dynamic queue test suite
2024-09-04 15:49 ` [PATCH v4 0/2] dts: add dynamic queue configuration test suite jspewock
2024-09-04 15:49 ` [PATCH v4 1/2] dts: add port queue modification and forwarding stats to testpmd jspewock
@ 2024-09-04 15:49 ` jspewock
1 sibling, 0 replies; 81+ messages in thread
From: jspewock @ 2024-09-04 15:49 UTC (permalink / raw)
To: wathsala.vithanage, probb, Luca.Vizzarro, alex.chapman,
juraj.linkes, thomas, paul.szczepanek, yoan.picchi, npratte,
Honnappa.Nagarahalli
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds a new test suite that is designed to test the stopping
and modification of port queues at runtime. Specifically, there are
test cases that display the ports ability to stop some queues but still
send and receive traffic on others, as well as the ability to configure
the ring size of the queue without blocking the traffic on other queues.
Depends-on: patch-143594 ("dts: add send_packets to test suites and
rework packet addressing")
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 286 +++++++++++++++++++++
2 files changed, 288 insertions(+), 1 deletion(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index f02a310bb5..d83a2f51c5 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -187,7 +187,8 @@
"enum": [
"hello_world",
"os_udp",
- "pmd_buffer_scatter"
+ "pmd_buffer_scatter",
+ "dynamic_queue_conf"
]
},
"test_target": {
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
new file mode 100644
index 0000000000..f5c667cdeb
--- /dev/null
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -0,0 +1,286 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 University of New Hampshire
+
+"""Dynamic configuration of port queues test suite.
+
+This test suite tests the support of being able to either stop or reconfigure port queues at
+runtime without stopping the entire device. Previously, to configure a DPDK ethdev, the application
+first specifies how many Tx and Rx queues to include in the ethdev and then application sets up
+each queue individually. Only once all the queues have been set up can the application then start
+the device, and at this point traffic can flow. If device stops, this halts the flow of traffic on
+all queues in the ethdev completely. Dynamic queue is a capability present on some NICs that
+specifies whether the NIC is able to delay the configuration of queues on its port. This capability
+allows for the support of stopping and reconfiguring queues on a port at runtime without stopping
+the entire device.
+
+Support of this capability is shown by starting the Poll Mode Driver with multiple Rx and Tx queues
+configured and stopping some prior to forwarding packets, then examining whether or not the stopped
+ports and the unmodified ports were able to handle traffic. In addition to just stopping the ports,
+the ports must also show that they support configuration changes on their queues at runtime without
+stopping the entire device. This is shown by changing the ring size of the queues.
+
+If the Poll Mode Driver is able to stop some queues on a port and modify them then handle traffic
+on the unmodified queues while the others are stopped, then it is the case that the device properly
+supports dynamic configuration of its queues.
+"""
+
+import random
+from typing import Callable, ClassVar, MutableSet
+
+from scapy.layers.inet import IP # type: ignore[import-untyped]
+from scapy.layers.l2 import Ether # type: ignore[import-untyped]
+from scapy.packet import Raw # type: ignore[import-untyped]
+
+from framework.exception import InteractiveCommandExecutionError
+from framework.params.testpmd import PortTopology, SimpleForwardingModes
+from framework.remote_session.testpmd_shell import TestPmdShell
+from framework.test_suite import TestSuite
+
+
+def setup_and_teardown_test(
+ test_meth: Callable[
+ ["TestDynamicQueueConf", int, MutableSet, MutableSet, TestPmdShell, bool], None
+ ],
+) -> Callable[["TestDynamicQueueConf", bool], None]:
+ """Decorator that provides a setup and teardown for testing methods.
+
+ This decorator provides a method that sets up the environment for testing, runs the test
+ method, and then does a clean-up verification step after the queues are started again. The
+ decorated method will be provided with all the variables it should need to run testing
+ including: The ID of the port where the queues for testing reside, disjoint sets of IDs for
+ queues that are/aren't modified, a testpmd session to run testing with, and a flag that
+ indicates whether or not testing should be done on Rx or Tx queues.
+
+ Args:
+ test_meth: The decorated method that tests configuration of port queues at runtime.
+ This method must have the following parameters in order: An int that represents a
+ port ID, a set of queues for testing, a set of unmodified queues, a testpmd
+ interactive shell, and a boolean that, when :data:`True`, does Rx testing,
+ otherwise does Tx testing. This method must also be a member of the
+ :class:`TestDynamicQueueConf` class.
+
+ Returns:
+ A method that sets up the environment, runs the decorated method, then re-enables all
+ queues and validates they can still handle traffic.
+ """
+
+ def wrap(self: "TestDynamicQueueConf", is_rx_testing: bool) -> None:
+ """Setup environment, run test function, then cleanup.
+
+ Start a testpmd shell and stop ports for testing, then call the decorated function that
+ performs the testing. After the decorated function is finished running its testing,
+ start the stopped queues and send packets to validate that these ports can properly
+ handle traffic after being started again.
+
+ Args:
+ self: Instance of :class:`TestDynamicQueueConf` `test_meth` belongs to.
+ is_rx_testing: If :data:`True` then Rx queues will be the ones modified throughout
+ the test, otherwise Tx queues will be modified.
+ """
+ port_id = self.rx_port_num if is_rx_testing else self.tx_port_num
+ queues_to_config: set[int] = set()
+ while len(queues_to_config) < self.num_ports_to_modify:
+ queues_to_config.add(random.randint(1, self.number_of_queues - 1))
+ unchanged_queues = set(range(self.number_of_queues)) - queues_to_config
+ with TestPmdShell(
+ self.sut_node,
+ port_topology=PortTopology.chained,
+ rx_queues=self.number_of_queues,
+ tx_queues=self.number_of_queues,
+ ) as testpmd:
+ for q in queues_to_config:
+ testpmd.stop_port_queue(port_id, q, is_rx_testing)
+ testpmd.set_forward_mode(SimpleForwardingModes.mac)
+
+ test_meth(self, port_id, queues_to_config, unchanged_queues, testpmd, is_rx_testing)
+
+ for queue_id in queues_to_config:
+ testpmd.start_port_queue(port_id, queue_id, is_rx_testing)
+
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+ for queue_id in queues_to_config:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, queue_id, forwarding_stats),
+ f"Modified queue {queue_id} on port {port_id} failed to receive traffic after"
+ "being started again.",
+ )
+
+ return wrap
+
+
+class TestDynamicQueueConf(TestSuite):
+ """DPDK dynamic queue configuration test suite.
+
+ Testing for the support of dynamic queue configuration is done by splitting testing by the type
+ of queue (either Rx or Tx) and the type of testing (testing for stopping a port at runtime vs
+ testing configuration changes at runtime). Testing is done by first stopping a finite number of
+ port queues (3 is sufficient) and either modifying the configuration or sending packets to
+ verify that the unmodified queues can handle traffic. Specifically, the following cases are
+ tested:
+
+ 1. The application should be able to start the device with only some of the
+ queues set up.
+ 2. The application should be able to reconfigure existing queues at runtime
+ without calling dev_stop().
+ """
+
+ #:
+ num_ports_to_modify: ClassVar[int] = 3
+ #: Source IP address to use when sending packets.
+ src_addr: ClassVar[str] = "192.168.0.1"
+ #: Subnet to use for all of the destination addresses of the packets being sent.
+ dst_address_subnet: ClassVar[str] = "192.168.1"
+ #: ID of the port to modify Rx queues on.
+ rx_port_num: ClassVar[int] = 0
+ #: ID of the port to modify Tx queues on.
+ tx_port_num: ClassVar[int] = 1
+ #: Number of queues to start testpmd with. There will be the same number of Rx and Tx queues.
+ #: 8 was chosen as a number that is low enough for most NICs to accommodate while also being
+ #: enough to validate the usage of the queues.
+ number_of_queues: ClassVar[int] = 8
+ #: The number of packets to send while testing. The test calls for well over the ring size - 1
+ #: packets in the modification test case and the only options for ring size are 256 or 512,
+ #: therefore 1024 will be more than enough.
+ number_of_packets_to_send: ClassVar[int] = 1024
+
+ def send_packets_with_different_addresses(self, number_of_packets: int) -> None:
+ """Send a set number of packets each with different dst addresses.
+
+ Different destination addresses are required to ensure that each queue is used. If every
+ packet had the same address, then they would all be processed by the same queue. Note that
+ this means the current implementation of this method is limited to only work for up to 254
+ queues. A smaller subnet would be required to handle an increased number of queues.
+
+ Args:
+ number_of_packets: The number of packets to generate and then send using the traffic
+ generator.
+ """
+ packets_to_send = [
+ Ether()
+ / IP(src=self.src_addr, dst=f"{self.dst_address_subnet}.{(i % 254) + 1}")
+ / Raw()
+ for i in range(number_of_packets)
+ ]
+ self.send_packets(packets_to_send)
+
+ def port_queue_in_stats(
+ self, port_id: int, is_rx_queue: bool, queue_id: int, stats: str
+ ) -> bool:
+ """Verify if stats for a queue are in the provided output.
+
+ Args:
+ port_id: ID of the port that the queue resides on.
+ is_rx_queue: Type of queue to scan for, if :data:`True` then search for an Rx queue,
+ otherwise search for a Tx queue.
+ queue_id: ID of the queue.
+ stats: Testpmd forwarding statistics to scan for the given queue.
+
+ Returns:
+ If the queue appeared in the forwarding statistics.
+ """
+ type_of_queue = "RX" if is_rx_queue else "TX"
+ return f"{type_of_queue} Port= {port_id}/Queue={queue_id:2d}" in stats
+
+ @setup_and_teardown_test
+ def modify_ring_size(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify ring size of port queues can be configured at runtime.
+
+ Ring size of queues in `queues_to_modify` are set to 512 unless that is already their
+ configured size, in which case they are instead set to 256. Queues in `queues_to_modify`
+ are expected to already be stopped before calling this method. `testpmd` is also expected
+ to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ for queue_id in queues_to_modify:
+ curr_ring_size = testpmd.show_port_queue_info(
+ port_id, queue_id, is_rx_testing
+ ).ring_size
+ new_ring_size = 256 if curr_ring_size == 512 else 512
+ try:
+ testpmd.set_queue_ring_size(
+ port_id, queue_id, new_ring_size, is_rx_testing, verify=True
+ )
+ # The testpmd method verifies that the modification worked, so we catch that error
+ # and just re-raise it as a test case failure
+ except InteractiveCommandExecutionError:
+ self.verify(
+ False,
+ f"Failed to update the ring size of queue {queue_id} on port "
+ f"{port_id} at runtime",
+ )
+
+ @setup_and_teardown_test
+ def stop_queues(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify stopped queues do not handle traffic and do not block traffic on other queues.
+
+ Queues in `queues_to_modify` are expected to already be stopped before calling this method.
+ `testpmd` is also expected to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+
+ # Checking that all unmodified queues handled some packets is important because this
+ # test case checks for the absence of stopped queues to validate that they cannot
+ # receive traffic. If there are some unchanged queues that also didn't receive traffic,
+ # it means there could be another reason for the packets not transmitting and,
+ # therefore, a false positive result.
+ for unchanged_q_id in unchanged_queues:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, unchanged_q_id, forwarding_stats),
+ f"Queue {unchanged_q_id} failed to receive traffic.",
+ )
+ for stopped_q_id in queues_to_modify:
+ self.verify(
+ not self.port_queue_in_stats(
+ port_id, is_rx_testing, stopped_q_id, forwarding_stats
+ ),
+ f"Queue {stopped_q_id} should be stopped but still received traffic.",
+ )
+
+ def test_rx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`True`."""
+ self.stop_queues(True)
+
+ def test_rx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`True`."""
+ self.modify_ring_size(True)
+
+ def test_tx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`False`."""
+ self.stop_queues(False)
+
+ def test_tx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`False`."""
+ self.modify_ring_size(False)
--
2.46.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v5 0/2] dts: add dynamic queue configuration test suite
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
` (6 preceding siblings ...)
2024-09-04 15:49 ` [PATCH v4 0/2] dts: add dynamic queue configuration test suite jspewock
@ 2024-09-25 19:20 ` jspewock
2024-09-25 19:20 ` [PATCH v5 1/2] dts: add port queue modification and forwarding stats to testpmd jspewock
` (3 more replies)
7 siblings, 4 replies; 81+ messages in thread
From: jspewock @ 2024-09-25 19:20 UTC (permalink / raw)
To: yoan.picchi, npratte, wathsala.vithanage, probb,
Honnappa.Nagarahalli, paul.szczepanek, thomas, juraj.linkes,
Luca.Vizzarro, alex.chapman
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
v5:
* applied on next-dts
Jeremy Spewock (2):
dts: add port queue modification and forwarding stats to testpmd
dts: add dynamic queue test suite
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/framework/remote_session/testpmd_shell.py | 233 +++++++++++++-
dts/tests/TestSuite_dynamic_queue_conf.py | 286 ++++++++++++++++++
3 files changed, 519 insertions(+), 3 deletions(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
--
2.46.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v5 1/2] dts: add port queue modification and forwarding stats to testpmd
2024-09-25 19:20 ` [PATCH v5 0/2] dts: add dynamic queue configuration " jspewock
@ 2024-09-25 19:20 ` jspewock
2024-09-25 19:20 ` [PATCH v5 2/2] dts: add dynamic queue test suite jspewock
` (2 subsequent siblings)
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-09-25 19:20 UTC (permalink / raw)
To: yoan.picchi, npratte, wathsala.vithanage, probb,
Honnappa.Nagarahalli, paul.szczepanek, thomas, juraj.linkes,
Luca.Vizzarro, alex.chapman
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds methods for querying and modifying port queue state and
configuration. In addition to this, it also adds the ability to capture
the forwarding statistics that get outputted when you send the "stop"
command in testpmd. Querying of port queue information is handled
through a TextParser dataclass in case there is future need for using
more of the output from the command used to query the information.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
The rxq info command has been added to the capabilities patch series
so this version should likely extend from that to utilize that version
of the TextParser if it is preferred. Additionally, the test suite
itself would benefit from a capability check to make sure individual
queues can be stopped/started/modified. I will look into adding this in
a future version.
dts/framework/remote_session/testpmd_shell.py | 233 +++++++++++++++++-
1 file changed, 231 insertions(+), 2 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 77902a468d..119c5e0cba 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -20,7 +20,7 @@
from dataclasses import dataclass, field
from enum import Flag, auto
from pathlib import PurePath
-from typing import Any, Callable, ClassVar, Concatenate, ParamSpec
+from typing import Any, Callable, ClassVar, Concatenate, ParamSpec, cast
from typing_extensions import Self, Unpack
@@ -545,6 +545,56 @@ class TestPmdPort(TextParser):
)
+@dataclass
+class TestPmdPortQueue(TextParser):
+ """Dataclass representation of the common parts of the testpmd `show rxq/txq info` commands."""
+
+ #:
+ prefetch_threshold: int = field(metadata=TextParser.find_int(r"prefetch threshold: (\d+)"))
+ #:
+ host_threshold: int = field(metadata=TextParser.find_int(r"host threshold: (\d+)"))
+ #:
+ writeback_threshold: int = field(metadata=TextParser.find_int(r"writeback threshold: (\d+)"))
+ #:
+ free_threshold: int = field(metadata=TextParser.find_int(r"free threshold: (\d+)"))
+ #:
+ deferred_start: bool = field(metadata=TextParser.find("deferred start: on"))
+ #: The number of RXD/TXDs is just the ring size of the queue.
+ ring_size: int = field(metadata=TextParser.find_int(r"Number of (?:RXDs|TXDs): (\d+)"))
+ #:
+ is_queue_started: bool = field(metadata=TextParser.find("queue state: started"))
+ #:
+ burst_mode: str | None = field(
+ default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)")
+ )
+
+
+@dataclass
+class TestPmdTxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show txq info` command."""
+
+ #:
+ rs_threshold: int | None = field(
+ default=None, metadata=TextParser.find_int(r"RS threshold: (\d+)")
+ )
+
+
+@dataclass
+class TestPmdRxPortQueue(TestPmdPortQueue):
+ """Dataclass representation for testpmd `show rxq info` command."""
+
+ #:
+ mempool: str | None = field(default=None, metadata=TextParser.find(r"Mempool: ([^\r\n]+)"))
+ #:
+ can_drop_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"drop packets: on")
+ )
+ #:
+ is_scattering_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"scattered packets: on")
+ )
+
+
@dataclass
class TestPmdPortStats(TextParser):
"""Port statistics."""
@@ -714,7 +764,7 @@ def start(self, verify: bool = True) -> None:
"Not all ports came up after starting packet forwarding in testpmd."
)
- def stop(self, verify: bool = True) -> None:
+ def stop(self, verify: bool = True) -> str:
"""Stop packet forwarding.
Args:
@@ -722,6 +772,9 @@ def stop(self, verify: bool = True) -> None:
forwarding was stopped successfully or not started. If neither is found, it is
considered an error.
+ Returns:
+ Output gathered from sending the stop command.
+
Raises:
InteractiveCommandExecutionError: If `verify` is :data:`True` and the command to stop
forwarding results in an error.
@@ -734,6 +787,7 @@ def stop(self, verify: bool = True) -> None:
):
self._logger.debug(f"Failed to stop packet forwarding: \n{stop_cmd_output}")
raise InteractiveCommandExecutionError("Testpmd failed to stop packet forwarding.")
+ return stop_cmd_output
def get_devices(self) -> list[TestPmdDevice]:
"""Get a list of device names that are known to testpmd.
@@ -981,6 +1035,181 @@ def set_port_mtu_all(self, mtu: int, verify: bool = True) -> None:
for port in self.ports:
self.set_port_mtu(port.id, mtu, verify)
+ def show_port_queue_info(
+ self, port_id: int, queue_id: int, is_rx_queue: bool
+ ) -> TestPmdPortQueue:
+ """Get the info for a queue on a given port.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+ is_rx_queue: Whether to check an RX or TX queue. If :data:`True` an RX queue will be
+ queried, otherwise a TX queue will be queried.
+
+ Raises:
+ InteractiveCommandExecutionError: If there is a failure when getting the info for the
+ queue.
+
+ Returns:
+ Information about the queue on the given port.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ queue_info = self.send_command(
+ f"show {queue_type} info {port_id} {queue_id}", skip_first_line=True
+ )
+ if queue_info.startswith("ETHDEV: Invalid"):
+ raise InteractiveCommandExecutionError(
+ f"Could not get the info for {queue_type} {queue_id} on port {port_id}"
+ )
+ return (
+ TestPmdRxPortQueue.parse(queue_info)
+ if is_rx_queue
+ else TestPmdTxPortQueue.parse(queue_info)
+ )
+
+ def show_port_rx_queue_info(self, port_id: int, queue_id: int) -> TestPmdRxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdRxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Rx queue on the given port.
+ """
+ return cast(TestPmdRxPortQueue, self.show_port_queue_info(port_id, queue_id, True))
+
+ def show_port_tx_queue_info(self, port_id: int, queue_id: int) -> TestPmdTxPortQueue:
+ """Get port queue info and cast to :class:`TestPmdTxPortQueue`.
+
+ Wrapper around :meth:`show_port_queue_info` that casts the more generic type into the
+ correct subclass.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to query.
+
+ Returns:
+ Information about the Tx queue on the given port.
+ """
+ return cast(TestPmdTxPortQueue, self.show_port_queue_info(port_id, queue_id, False))
+
+ def stop_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Stops a given queue on a port.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to stop.
+ is_rx_queue: Type of queue to stop. If :data:`True` an RX queue will be stopped,
+ otherwise a TX queue will be stopped.
+ verify: If :data:`True` an additional command will be sent to verify the queue stopped.
+ Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ stop.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ stop_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} stop")
+ if verify:
+ if self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to stop {port_type} {queue_id} on port {port_id}"
+ )
+
+ def start_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Starts a given queue on a port.
+
+ First sets up the port queue, then starts it.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to start.
+ is_rx_queue: Type of queue to start. If :data:`True` an RX queue will be started,
+ otherwise a TX queue will be started.
+ verify: if :data:`True` an additional command will be sent to verify that the queue was
+ started. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ start.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ start_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} start")
+ if verify:
+ if not self.show_port_queue_info(port_id, queue_id, is_rx_queue).is_queue_started:
+ self._logger.debug(
+ f"Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to start {port_type} {queue_id} on port {port_id}"
+ )
+
+ def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: bool) -> None:
+ """Setup a given queue on a port.
+
+ This functionality cannot be verified because the setup action only takes effect when the
+ queue is started.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to setup.
+ is_rx_queue: Type of queue to setup. If :data:`True` an RX queue will be setup,
+ otherwise a TX queue will be setup.
+ """
+ self.send_command(f"port {port_id} {'rxq' if is_rx_queue else 'txq'} {queue_id} setup")
+
+ def set_queue_ring_size(
+ self,
+ port_id: int,
+ queue_id: int,
+ size: int,
+ is_rx_queue: bool,
+ verify: bool = True,
+ ) -> None:
+ """Update the ring size of an Rx/Tx queue on a given port.
+
+ Queue is setup after setting the ring size so that the queue info reflects this change and
+ it can be verified.
+
+ Args:
+ port_id: The port that the queue resides on.
+ queue_id: The ID of the queue on the port.
+ size: The size to update the ring size to.
+ is_rx_queue: Whether to modify an RX or TX queue. If :data:`True` an RX queue will be
+ updated, otherwise a TX queue will be updated.
+ verify: If :data:`True` an additional command will be sent to check the ring size of
+ the queue in an attempt to validate that the size was changes properly.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and there is a failure
+ when updating ring size.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ self.send_command(f"port config {port_id} {queue_type} {queue_id} ring_size {size}")
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ if verify:
+ curr_ring_size = self.show_port_queue_info(port_id, queue_id, is_rx_queue).ring_size
+ if curr_ring_size != size:
+ self._logger.debug(
+ f"Failed up update ring size of queue {queue_id} on port {port_id}. Current"
+ f" ring size is {curr_ring_size}."
+ )
+ raise InteractiveCommandExecutionError(
+ f"Failed to update ring size of queue {queue_id} on port {port_id}"
+ )
+
def _close(self) -> None:
"""Overrides :meth:`~.interactive_shell.close`."""
self.stop()
--
2.46.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v5 2/2] dts: add dynamic queue test suite
2024-09-25 19:20 ` [PATCH v5 0/2] dts: add dynamic queue configuration " jspewock
2024-09-25 19:20 ` [PATCH v5 1/2] dts: add port queue modification and forwarding stats to testpmd jspewock
@ 2024-09-25 19:20 ` jspewock
2024-11-05 16:58 ` [PATCH v6 0/2] dts: add dynamic queue configuration " Dean Marx
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
3 siblings, 0 replies; 81+ messages in thread
From: jspewock @ 2024-09-25 19:20 UTC (permalink / raw)
To: yoan.picchi, npratte, wathsala.vithanage, probb,
Honnappa.Nagarahalli, paul.szczepanek, thomas, juraj.linkes,
Luca.Vizzarro, alex.chapman
Cc: dev, Jeremy Spewock
From: Jeremy Spewock <jspewock@iol.unh.edu>
This patch adds a new test suite that is designed to test the stopping
and modification of port queues at runtime. Specifically, there are
test cases that display the ports ability to stop some queues but still
send and receive traffic on others, as well as the ability to configure
the ring size of the queue without blocking the traffic on other queues.
Depends-on: patch-144441 ("dts: add send_packets to test_suite")
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 286 +++++++++++++++++++++
2 files changed, 288 insertions(+), 1 deletion(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index df390e8ae2..11c6f25aa1 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -187,7 +187,8 @@
"enum": [
"hello_world",
"os_udp",
- "pmd_buffer_scatter"
+ "pmd_buffer_scatter",
+ "dynamic_queue_conf"
]
},
"test_target": {
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
new file mode 100644
index 0000000000..f5c667cdeb
--- /dev/null
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -0,0 +1,286 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 University of New Hampshire
+
+"""Dynamic configuration of port queues test suite.
+
+This test suite tests the support of being able to either stop or reconfigure port queues at
+runtime without stopping the entire device. Previously, to configure a DPDK ethdev, the application
+first specifies how many Tx and Rx queues to include in the ethdev and then application sets up
+each queue individually. Only once all the queues have been set up can the application then start
+the device, and at this point traffic can flow. If device stops, this halts the flow of traffic on
+all queues in the ethdev completely. Dynamic queue is a capability present on some NICs that
+specifies whether the NIC is able to delay the configuration of queues on its port. This capability
+allows for the support of stopping and reconfiguring queues on a port at runtime without stopping
+the entire device.
+
+Support of this capability is shown by starting the Poll Mode Driver with multiple Rx and Tx queues
+configured and stopping some prior to forwarding packets, then examining whether or not the stopped
+ports and the unmodified ports were able to handle traffic. In addition to just stopping the ports,
+the ports must also show that they support configuration changes on their queues at runtime without
+stopping the entire device. This is shown by changing the ring size of the queues.
+
+If the Poll Mode Driver is able to stop some queues on a port and modify them then handle traffic
+on the unmodified queues while the others are stopped, then it is the case that the device properly
+supports dynamic configuration of its queues.
+"""
+
+import random
+from typing import Callable, ClassVar, MutableSet
+
+from scapy.layers.inet import IP # type: ignore[import-untyped]
+from scapy.layers.l2 import Ether # type: ignore[import-untyped]
+from scapy.packet import Raw # type: ignore[import-untyped]
+
+from framework.exception import InteractiveCommandExecutionError
+from framework.params.testpmd import PortTopology, SimpleForwardingModes
+from framework.remote_session.testpmd_shell import TestPmdShell
+from framework.test_suite import TestSuite
+
+
+def setup_and_teardown_test(
+ test_meth: Callable[
+ ["TestDynamicQueueConf", int, MutableSet, MutableSet, TestPmdShell, bool], None
+ ],
+) -> Callable[["TestDynamicQueueConf", bool], None]:
+ """Decorator that provides a setup and teardown for testing methods.
+
+ This decorator provides a method that sets up the environment for testing, runs the test
+ method, and then does a clean-up verification step after the queues are started again. The
+ decorated method will be provided with all the variables it should need to run testing
+ including: The ID of the port where the queues for testing reside, disjoint sets of IDs for
+ queues that are/aren't modified, a testpmd session to run testing with, and a flag that
+ indicates whether or not testing should be done on Rx or Tx queues.
+
+ Args:
+ test_meth: The decorated method that tests configuration of port queues at runtime.
+ This method must have the following parameters in order: An int that represents a
+ port ID, a set of queues for testing, a set of unmodified queues, a testpmd
+ interactive shell, and a boolean that, when :data:`True`, does Rx testing,
+ otherwise does Tx testing. This method must also be a member of the
+ :class:`TestDynamicQueueConf` class.
+
+ Returns:
+ A method that sets up the environment, runs the decorated method, then re-enables all
+ queues and validates they can still handle traffic.
+ """
+
+ def wrap(self: "TestDynamicQueueConf", is_rx_testing: bool) -> None:
+ """Setup environment, run test function, then cleanup.
+
+ Start a testpmd shell and stop ports for testing, then call the decorated function that
+ performs the testing. After the decorated function is finished running its testing,
+ start the stopped queues and send packets to validate that these ports can properly
+ handle traffic after being started again.
+
+ Args:
+ self: Instance of :class:`TestDynamicQueueConf` `test_meth` belongs to.
+ is_rx_testing: If :data:`True` then Rx queues will be the ones modified throughout
+ the test, otherwise Tx queues will be modified.
+ """
+ port_id = self.rx_port_num if is_rx_testing else self.tx_port_num
+ queues_to_config: set[int] = set()
+ while len(queues_to_config) < self.num_ports_to_modify:
+ queues_to_config.add(random.randint(1, self.number_of_queues - 1))
+ unchanged_queues = set(range(self.number_of_queues)) - queues_to_config
+ with TestPmdShell(
+ self.sut_node,
+ port_topology=PortTopology.chained,
+ rx_queues=self.number_of_queues,
+ tx_queues=self.number_of_queues,
+ ) as testpmd:
+ for q in queues_to_config:
+ testpmd.stop_port_queue(port_id, q, is_rx_testing)
+ testpmd.set_forward_mode(SimpleForwardingModes.mac)
+
+ test_meth(self, port_id, queues_to_config, unchanged_queues, testpmd, is_rx_testing)
+
+ for queue_id in queues_to_config:
+ testpmd.start_port_queue(port_id, queue_id, is_rx_testing)
+
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+ for queue_id in queues_to_config:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, queue_id, forwarding_stats),
+ f"Modified queue {queue_id} on port {port_id} failed to receive traffic after"
+ "being started again.",
+ )
+
+ return wrap
+
+
+class TestDynamicQueueConf(TestSuite):
+ """DPDK dynamic queue configuration test suite.
+
+ Testing for the support of dynamic queue configuration is done by splitting testing by the type
+ of queue (either Rx or Tx) and the type of testing (testing for stopping a port at runtime vs
+ testing configuration changes at runtime). Testing is done by first stopping a finite number of
+ port queues (3 is sufficient) and either modifying the configuration or sending packets to
+ verify that the unmodified queues can handle traffic. Specifically, the following cases are
+ tested:
+
+ 1. The application should be able to start the device with only some of the
+ queues set up.
+ 2. The application should be able to reconfigure existing queues at runtime
+ without calling dev_stop().
+ """
+
+ #:
+ num_ports_to_modify: ClassVar[int] = 3
+ #: Source IP address to use when sending packets.
+ src_addr: ClassVar[str] = "192.168.0.1"
+ #: Subnet to use for all of the destination addresses of the packets being sent.
+ dst_address_subnet: ClassVar[str] = "192.168.1"
+ #: ID of the port to modify Rx queues on.
+ rx_port_num: ClassVar[int] = 0
+ #: ID of the port to modify Tx queues on.
+ tx_port_num: ClassVar[int] = 1
+ #: Number of queues to start testpmd with. There will be the same number of Rx and Tx queues.
+ #: 8 was chosen as a number that is low enough for most NICs to accommodate while also being
+ #: enough to validate the usage of the queues.
+ number_of_queues: ClassVar[int] = 8
+ #: The number of packets to send while testing. The test calls for well over the ring size - 1
+ #: packets in the modification test case and the only options for ring size are 256 or 512,
+ #: therefore 1024 will be more than enough.
+ number_of_packets_to_send: ClassVar[int] = 1024
+
+ def send_packets_with_different_addresses(self, number_of_packets: int) -> None:
+ """Send a set number of packets each with different dst addresses.
+
+ Different destination addresses are required to ensure that each queue is used. If every
+ packet had the same address, then they would all be processed by the same queue. Note that
+ this means the current implementation of this method is limited to only work for up to 254
+ queues. A smaller subnet would be required to handle an increased number of queues.
+
+ Args:
+ number_of_packets: The number of packets to generate and then send using the traffic
+ generator.
+ """
+ packets_to_send = [
+ Ether()
+ / IP(src=self.src_addr, dst=f"{self.dst_address_subnet}.{(i % 254) + 1}")
+ / Raw()
+ for i in range(number_of_packets)
+ ]
+ self.send_packets(packets_to_send)
+
+ def port_queue_in_stats(
+ self, port_id: int, is_rx_queue: bool, queue_id: int, stats: str
+ ) -> bool:
+ """Verify if stats for a queue are in the provided output.
+
+ Args:
+ port_id: ID of the port that the queue resides on.
+ is_rx_queue: Type of queue to scan for, if :data:`True` then search for an Rx queue,
+ otherwise search for a Tx queue.
+ queue_id: ID of the queue.
+ stats: Testpmd forwarding statistics to scan for the given queue.
+
+ Returns:
+ If the queue appeared in the forwarding statistics.
+ """
+ type_of_queue = "RX" if is_rx_queue else "TX"
+ return f"{type_of_queue} Port= {port_id}/Queue={queue_id:2d}" in stats
+
+ @setup_and_teardown_test
+ def modify_ring_size(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify ring size of port queues can be configured at runtime.
+
+ Ring size of queues in `queues_to_modify` are set to 512 unless that is already their
+ configured size, in which case they are instead set to 256. Queues in `queues_to_modify`
+ are expected to already be stopped before calling this method. `testpmd` is also expected
+ to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ for queue_id in queues_to_modify:
+ curr_ring_size = testpmd.show_port_queue_info(
+ port_id, queue_id, is_rx_testing
+ ).ring_size
+ new_ring_size = 256 if curr_ring_size == 512 else 512
+ try:
+ testpmd.set_queue_ring_size(
+ port_id, queue_id, new_ring_size, is_rx_testing, verify=True
+ )
+ # The testpmd method verifies that the modification worked, so we catch that error
+ # and just re-raise it as a test case failure
+ except InteractiveCommandExecutionError:
+ self.verify(
+ False,
+ f"Failed to update the ring size of queue {queue_id} on port "
+ f"{port_id} at runtime",
+ )
+
+ @setup_and_teardown_test
+ def stop_queues(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify stopped queues do not handle traffic and do not block traffic on other queues.
+
+ Queues in `queues_to_modify` are expected to already be stopped before calling this method.
+ `testpmd` is also expected to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+
+ # Checking that all unmodified queues handled some packets is important because this
+ # test case checks for the absence of stopped queues to validate that they cannot
+ # receive traffic. If there are some unchanged queues that also didn't receive traffic,
+ # it means there could be another reason for the packets not transmitting and,
+ # therefore, a false positive result.
+ for unchanged_q_id in unchanged_queues:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, unchanged_q_id, forwarding_stats),
+ f"Queue {unchanged_q_id} failed to receive traffic.",
+ )
+ for stopped_q_id in queues_to_modify:
+ self.verify(
+ not self.port_queue_in_stats(
+ port_id, is_rx_testing, stopped_q_id, forwarding_stats
+ ),
+ f"Queue {stopped_q_id} should be stopped but still received traffic.",
+ )
+
+ def test_rx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`True`."""
+ self.stop_queues(True)
+
+ def test_rx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`True`."""
+ self.modify_ring_size(True)
+
+ def test_tx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`False`."""
+ self.stop_queues(False)
+
+ def test_tx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`False`."""
+ self.modify_ring_size(False)
--
2.46.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 0/2] dts: add dynamic queue configuration test suite
2024-09-25 19:20 ` [PATCH v5 0/2] dts: add dynamic queue configuration " jspewock
2024-09-25 19:20 ` [PATCH v5 1/2] dts: add port queue modification and forwarding stats to testpmd jspewock
2024-09-25 19:20 ` [PATCH v5 2/2] dts: add dynamic queue test suite jspewock
@ 2024-11-05 16:58 ` Dean Marx
2024-11-05 16:58 ` [PATCH v6 1/2] dts: add port queue modification and forwarding stats to testpmd Dean Marx
2024-11-05 16:58 ` [PATCH v6 2/2] dts: add dynamic queue test suite Dean Marx
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
3 siblings, 2 replies; 81+ messages in thread
From: Dean Marx @ 2024-11-05 16:58 UTC (permalink / raw)
To: probb, npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
This patch ports over a test suite from old DTS that tests the
implementation of runtime port queue configuration of PMDs. It also
includes necessary framework changes to allow this to happen such as:
* Sending multiple packets and not capturing the result
* Only adjusting addresses of packets if they are still their default
values (i.e. not modified by the developer beforehand)
* Testpmd methods for port queue configuration and querying
v5:
* applied on next-dts
v6:
* rebased off of capabilities dataclass and methods
Dean Marx (2):
dts: add port queue modification and forwarding stats to testpmd
dts: add dynamic queue test suite
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/framework/remote_session/testpmd_shell.py | 216 +++++++++++--
dts/tests/TestSuite_dynamic_queue_conf.py | 293 ++++++++++++++++++
3 files changed, 477 insertions(+), 35 deletions(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
--
2.44.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 1/2] dts: add port queue modification and forwarding stats to testpmd
2024-11-05 16:58 ` [PATCH v6 0/2] dts: add dynamic queue configuration " Dean Marx
@ 2024-11-05 16:58 ` Dean Marx
2024-11-18 23:25 ` Patrick Robb
2024-11-18 23:36 ` Patrick Robb
2024-11-05 16:58 ` [PATCH v6 2/2] dts: add dynamic queue test suite Dean Marx
1 sibling, 2 replies; 81+ messages in thread
From: Dean Marx @ 2024-11-05 16:58 UTC (permalink / raw)
To: probb, npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx, Jeremy Spewock
This patch adds methods for querying and modifying port queue state and
configuration. In addition to this, it also adds the ability to capture
the forwarding statistics that get outputted when you send the "stop"
command in testpmd. Querying of port queue information is handled
through a TextParser dataclass in case there is future need for using
more of the output from the command used to query the information.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/framework/remote_session/testpmd_shell.py | 216 +++++++++++++++---
1 file changed, 182 insertions(+), 34 deletions(-)
diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py
index 16b41a7814..995ba17ec4 100644
--- a/dts/framework/remote_session/testpmd_shell.py
+++ b/dts/framework/remote_session/testpmd_shell.py
@@ -425,7 +425,46 @@ def make_parser(cls) -> ParserFn:
@dataclass
-class TestPmdRxqInfo(TextParser):
+class TestPmdQueueInfo(TextParser):
+ """Dataclass representation of the common parts of the testpmd `show rxq/txq info` commands."""
+
+ #:
+ prefetch_threshold: int = field(metadata=TextParser.find_int(r"prefetch threshold: (\d+)"))
+ #:
+ host_threshold: int = field(metadata=TextParser.find_int(r"host threshold: (\d+)"))
+ #:
+ writeback_threshold: int = field(metadata=TextParser.find_int(r"writeback threshold: (\d+)"))
+ #:
+ free_threshold: int = field(metadata=TextParser.find_int(r"free threshold: (\d+)"))
+ #:
+ deferred_start: bool = field(metadata=TextParser.find("deferred start: on"))
+ #: The number of RXD/TXDs is just the ring size of the queue.
+ ring_size: int = field(metadata=TextParser.find_int(r"Number of (?:RXDs|TXDs): (\d+)"))
+ #:
+ is_queue_started: bool = field(metadata=TextParser.find("queue state: started"))
+ #:
+ burst_mode: str | None = field(
+ default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)")
+ )
+
+
+@dataclass
+class TestPmdTxqInfo(TestPmdQueueInfo):
+ """Representation of testpmd's ``show txq info <port_id> <queue_id>`` command.
+
+ References:
+ testpmd command function: ``app/test-pmd/cmdline.c:cmd_showqueue()``
+ testpmd display function: ``app/test-pmd/config.c:rx_queue_infos_display()``
+ """
+
+ #: Ring size threshold
+ rs_threshold: int | None = field(
+ default=None, metadata=TextParser.find_int(r"TX RS threshold: (\d+)\b")
+ )
+
+
+@dataclass
+class TestPmdRxqInfo(TestPmdQueueInfo):
"""Representation of testpmd's ``show rxq info <port_id> <queue_id>`` command.
References:
@@ -433,42 +472,18 @@ class TestPmdRxqInfo(TextParser):
testpmd display function: ``app/test-pmd/config.c:rx_queue_infos_display()``
"""
- #:
- port_id: int = field(metadata=TextParser.find_int(r"Infos for port (\d+)\b ?, RX queue \d+\b"))
- #:
- queue_id: int = field(metadata=TextParser.find_int(r"Infos for port \d+\b ?, RX queue (\d+)\b"))
#: Mempool used by that queue
- mempool: str = field(metadata=TextParser.find(r"Mempool: ([^\r\n]+)"))
- #: Ring prefetch threshold
- rx_prefetch_threshold: int = field(
- metadata=TextParser.find_int(r"RX prefetch threshold: (\d+)\b")
- )
- #: Ring host threshold
- rx_host_threshold: int = field(metadata=TextParser.find_int(r"RX host threshold: (\d+)\b"))
- #: Ring writeback threshold
- rx_writeback_threshold: int = field(
- metadata=TextParser.find_int(r"RX writeback threshold: (\d+)\b")
- )
- #: Drives the freeing of Rx descriptors
- rx_free_threshold: int = field(metadata=TextParser.find_int(r"RX free threshold: (\d+)\b"))
+ mempool: str | None = field(default=None, metadata=TextParser.find(r"Mempool: ([^\r\n]+)"))
#: Drop packets if no descriptors are available
- rx_drop_packets: bool = field(metadata=TextParser.find(r"RX drop packets: on"))
- #: Do not start queue with rte_eth_dev_start()
- rx_deferred_start: bool = field(metadata=TextParser.find(r"RX deferred start: on"))
- #: Scattered packets Rx enabled
- rx_scattered_packets: bool = field(metadata=TextParser.find(r"RX scattered packets: on"))
- #: The state of the queue
- rx_queue_state: str = field(metadata=RxQueueState.make_parser())
- #: Configured number of RXDs
- number_of_rxds: int = field(metadata=TextParser.find_int(r"Number of RXDs: (\d+)\b"))
- #: Hardware receive buffer size
- rx_buffer_size: int | None = field(
- default=None, metadata=TextParser.find_int(r"RX buffer size: (\d+)\b")
+ drop_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"RX drop packets: on")
)
- #: Burst mode information
- burst_mode: str | None = field(
- default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)")
+ #: Scattered packets Rx enabled
+ scattered_packets: bool | None = field(
+ default=None, metadata=TextParser.find(r"RX scattered packets: on")
)
+ #: The state of the queue
+ queue_state: str | None = field(default=None, metadata=RxQueueState.make_parser())
@dataclass
@@ -1975,6 +1990,139 @@ def get_capabilities_rx_offload(
rx_offload_capabilities.per_port | rx_offload_capabilities.per_queue,
)
+ def get_port_queue_info(
+ self, port_id: int, queue_id: int, is_rx_queue: bool
+ ) -> TestPmdQueueInfo:
+ """Returns the current state of the specified queue."""
+ command = f"show {'rxq' if is_rx_queue else 'txq'} info {port_id} {queue_id}"
+ queue_info = TestPmdQueueInfo.parse(self.send_command(command))
+ return queue_info
+
+ def setup_port_queue(self, port_id: int, queue_id: int, is_rx_queue: bool) -> None:
+ """Setup a given queue on a port.
+
+ This functionality cannot be verified because the setup action only takes effect when the
+ queue is started.
+
+ Args:
+ port_id: ID of the port where the queue resides.
+ queue_id: ID of the queue to setup.
+ is_rx_queue: Type of queue to setup. If :data:`True` an RX queue will be setup,
+ otherwise a TX queue will be setup.
+ """
+ self.send_command(f"port {port_id} {'rxq' if is_rx_queue else 'txq'} {queue_id} setup")
+
+ def stop_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Stops a given queue on a port.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to stop.
+ is_rx_queue: Type of queue to stop. If :data:`True` an RX queue will be stopped,
+ otherwise a TX queue will be stopped.
+ verify: If :data:`True` an additional command will be sent to verify the queue stopped.
+ Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ stop.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ stop_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} stop")
+ if verify:
+ queue_started = self.get_port_queue_info(
+ port_id, queue_id, is_rx_queue
+ ).is_queue_started
+ if queue_started:
+ self._logger.debug(
+ f"Failed to stop {port_type} {queue_id} on port {port_id}:\n{stop_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to stop {port_type} {queue_id} on port {port_id}"
+ )
+
+ def start_port_queue(
+ self, port_id: int, queue_id: int, is_rx_queue: bool, verify: bool = True
+ ) -> None:
+ """Starts a given queue on a port.
+
+ First sets up the port queue, then starts it.
+
+ Args:
+ port_id: ID of the port that the queue belongs to.
+ queue_id: ID of the queue to start.
+ is_rx_queue: Type of queue to start. If :data:`True` an RX queue will be started,
+ otherwise a TX queue will be started.
+ verify: if :data:`True` an additional command will be sent to verify that the queue was
+ started. Defaults to :data:`True`.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and the queue fails to
+ start.
+ """
+ port_type = "rxq" if is_rx_queue else "txq"
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ start_cmd_output = self.send_command(f"port {port_id} {port_type} {queue_id} start")
+ if verify:
+ queue_started = self.get_port_queue_info(
+ port_id, queue_id, is_rx_queue
+ ).is_queue_started
+ if not queue_started:
+ self._logger.debug(
+ f"Failed to start {port_type} {queue_id} on port {port_id}:\n{start_cmd_output}"
+ )
+ raise InteractiveCommandExecutionError(
+ f"Test pmd failed to start {port_type} {queue_id} on port {port_id}"
+ )
+
+ def get_queue_ring_size(self, port_id: int, queue_id: int, is_rx_queue: bool) -> int:
+ """Returns the current size of the ring on the specified queue."""
+ command = f"show {'rxq' if is_rx_queue else 'txq'} info {port_id} {queue_id}"
+ queue_info = TestPmdQueueInfo.parse(self.send_command(command))
+ return queue_info.ring_size
+
+ def set_queue_ring_size(
+ self,
+ port_id: int,
+ queue_id: int,
+ size: int,
+ is_rx_queue: bool,
+ verify: bool = True,
+ ) -> None:
+ """Update the ring size of an Rx/Tx queue on a given port.
+
+ Queue is setup after setting the ring size so that the queue info reflects this change and
+ it can be verified.
+
+ Args:
+ port_id: The port that the queue resides on.
+ queue_id: The ID of the queue on the port.
+ size: The size to update the ring size to.
+ is_rx_queue: Whether to modify an RX or TX queue. If :data:`True` an RX queue will be
+ updated, otherwise a TX queue will be updated.
+ verify: If :data:`True` an additional command will be sent to check the ring size of
+ the queue in an attempt to validate that the size was changes properly.
+
+ Raises:
+ InteractiveCommandExecutionError: If `verify` is :data:`True` and there is a failure
+ when updating ring size.
+ """
+ queue_type = "rxq" if is_rx_queue else "txq"
+ self.send_command(f"port config {port_id} {queue_type} {queue_id} ring_size {size}")
+ self.setup_port_queue(port_id, queue_id, is_rx_queue)
+ if verify:
+ curr_ring_size = self.get_queue_ring_size(port_id, queue_id, is_rx_queue)
+ if curr_ring_size != size:
+ self._logger.debug(
+ f"Failed up update ring size of queue {queue_id} on port {port_id}. Current"
+ f" ring size is {curr_ring_size}."
+ )
+ raise InteractiveCommandExecutionError(
+ f"Failed to update ring size of queue {queue_id} on port {port_id}"
+ )
+
def _update_capabilities_from_flag(
self,
supported_capabilities: MutableSet["NicCapability"],
@@ -2004,7 +2152,7 @@ def get_capabilities_rxq_info(
self._logger.debug("Getting rxq capabilities.")
command = f"show rxq info {self.ports[0].id} 0"
rxq_info = TestPmdRxqInfo.parse(self.send_command(command))
- if rxq_info.rx_scattered_packets:
+ if rxq_info.scattered_packets:
supported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED)
else:
unsupported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED)
--
2.44.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 2/2] dts: add dynamic queue test suite
2024-11-05 16:58 ` [PATCH v6 0/2] dts: add dynamic queue configuration " Dean Marx
2024-11-05 16:58 ` [PATCH v6 1/2] dts: add port queue modification and forwarding stats to testpmd Dean Marx
@ 2024-11-05 16:58 ` Dean Marx
2024-11-18 23:24 ` Patrick Robb
2024-11-18 23:24 ` Patrick Robb
1 sibling, 2 replies; 81+ messages in thread
From: Dean Marx @ 2024-11-05 16:58 UTC (permalink / raw)
To: probb, npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx, Jeremy Spewock
This patch adds a new test suite that is designed to test the stopping
and modification of port queues at runtime. Specifically, there are
test cases that display the ports ability to stop some queues but still
send and receive traffic on others, as well as the ability to configure
the ring size of the queue without blocking the traffic on other queues.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/framework/config/conf_yaml_schema.json | 3 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 293 +++++++++++++++++++++
2 files changed, 295 insertions(+), 1 deletion(-)
create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index cc3e78cef5..56785e5b79 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -244,7 +244,8 @@
"hello_world",
"os_udp",
"pmd_buffer_scatter",
- "vlan"
+ "vlan",
+ "dynamic_queue_conf"
]
},
"test_target": {
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
new file mode 100644
index 0000000000..ac7b81b602
--- /dev/null
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -0,0 +1,293 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 University of New Hampshire
+
+"""Dynamic configuration of port queues test suite.
+
+This test suite tests the support of being able to either stop or reconfigure port queues at
+runtime without stopping the entire device. Previously, to configure a DPDK ethdev, the application
+first specifies how many Tx and Rx queues to include in the ethdev and then application sets up
+each queue individually. Only once all the queues have been set up can the application then start
+the device, and at this point traffic can flow. If device stops, this halts the flow of traffic on
+all queues in the ethdev completely. Dynamic queue is a capability present on some NICs that
+specifies whether the NIC is able to delay the configuration of queues on its port. This capability
+allows for the support of stopping and reconfiguring queues on a port at runtime without stopping
+the entire device.
+
+Support of this capability is shown by starting the Poll Mode Driver with multiple Rx and Tx queues
+configured and stopping some prior to forwarding packets, then examining whether or not the stopped
+ports and the unmodified ports were able to handle traffic. In addition to just stopping the ports,
+the ports must also show that they support configuration changes on their queues at runtime without
+stopping the entire device. This is shown by changing the ring size of the queues.
+
+If the Poll Mode Driver is able to stop some queues on a port and modify them then handle traffic
+on the unmodified queues while the others are stopped, then it is the case that the device properly
+supports dynamic configuration of its queues.
+"""
+
+import random
+from typing import Callable, ClassVar, MutableSet
+
+from scapy.layers.inet import IP # type: ignore[import-untyped]
+from scapy.layers.l2 import Ether # type: ignore[import-untyped]
+from scapy.packet import Raw # type: ignore[import-untyped]
+
+from framework.exception import InteractiveCommandExecutionError
+from framework.params.testpmd import PortTopology, SimpleForwardingModes
+from framework.remote_session.testpmd_shell import TestPmdShell
+from framework.test_suite import TestSuite, func_test
+from framework.testbed_model.capability import NicCapability, requires
+
+
+def setup_and_teardown_test(
+ test_meth: Callable[
+ ["TestDynamicQueueConf", int, MutableSet, MutableSet, TestPmdShell, bool], None
+ ],
+) -> Callable[["TestDynamicQueueConf", bool], None]:
+ """Decorator that provides a setup and teardown for testing methods.
+
+ This decorator provides a method that sets up the environment for testing, runs the test
+ method, and then does a clean-up verification step after the queues are started again. The
+ decorated method will be provided with all the variables it should need to run testing
+ including: The ID of the port where the queues for testing reside, disjoint sets of IDs for
+ queues that are/aren't modified, a testpmd session to run testing with, and a flag that
+ indicates whether or not testing should be done on Rx or Tx queues.
+
+ Args:
+ test_meth: The decorated method that tests configuration of port queues at runtime.
+ This method must have the following parameters in order: An int that represents a
+ port ID, a set of queues for testing, a set of unmodified queues, a testpmd
+ interactive shell, and a boolean that, when :data:`True`, does Rx testing,
+ otherwise does Tx testing. This method must also be a member of the
+ :class:`TestDynamicQueueConf` class.
+
+ Returns:
+ A method that sets up the environment, runs the decorated method, then re-enables all
+ queues and validates they can still handle traffic.
+ """
+
+ def wrap(self: "TestDynamicQueueConf", is_rx_testing: bool) -> None:
+ """Setup environment, run test function, then cleanup.
+
+ Start a testpmd shell and stop ports for testing, then call the decorated function that
+ performs the testing. After the decorated function is finished running its testing,
+ start the stopped queues and send packets to validate that these ports can properly
+ handle traffic after being started again.
+
+ Args:
+ self: Instance of :class:`TestDynamicQueueConf` `test_meth` belongs to.
+ is_rx_testing: If :data:`True` then Rx queues will be the ones modified throughout
+ the test, otherwise Tx queues will be modified.
+ """
+ port_id = self.rx_port_num if is_rx_testing else self.tx_port_num
+ queues_to_config: set[int] = set()
+ while len(queues_to_config) < self.num_ports_to_modify:
+ queues_to_config.add(random.randint(1, self.number_of_queues - 1))
+ unchanged_queues = set(range(self.number_of_queues)) - queues_to_config
+ with TestPmdShell(
+ self.sut_node,
+ port_topology=PortTopology.chained,
+ rx_queues=self.number_of_queues,
+ tx_queues=self.number_of_queues,
+ ) as testpmd:
+ for q in queues_to_config:
+ testpmd.stop_port_queue(port_id, q, is_rx_testing)
+ testpmd.set_forward_mode(SimpleForwardingModes.mac)
+
+ test_meth(self, port_id, queues_to_config, unchanged_queues, testpmd, is_rx_testing)
+
+ for queue_id in queues_to_config:
+ testpmd.start_port_queue(port_id, queue_id, is_rx_testing)
+
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+ for queue_id in queues_to_config:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, queue_id, forwarding_stats),
+ f"Modified queue {queue_id} on port {port_id} failed to receive traffic after"
+ "being started again.",
+ )
+
+ return wrap
+
+
+class TestDynamicQueueConf(TestSuite):
+ """DPDK dynamic queue configuration test suite.
+
+ Testing for the support of dynamic queue configuration is done by splitting testing by the type
+ of queue (either Rx or Tx) and the type of testing (testing for stopping a port at runtime vs
+ testing configuration changes at runtime). Testing is done by first stopping a finite number of
+ port queues (3 is sufficient) and either modifying the configuration or sending packets to
+ verify that the unmodified queues can handle traffic. Specifically, the following cases are
+ tested:
+
+ 1. The application should be able to start the device with only some of the
+ queues set up.
+ 2. The application should be able to reconfigure existing queues at runtime
+ without calling dev_stop().
+ """
+
+ #:
+ num_ports_to_modify: ClassVar[int] = 3
+ #: Source IP address to use when sending packets.
+ src_addr: ClassVar[str] = "192.168.0.1"
+ #: Subnet to use for all of the destination addresses of the packets being sent.
+ dst_address_subnet: ClassVar[str] = "192.168.1"
+ #: ID of the port to modify Rx queues on.
+ rx_port_num: ClassVar[int] = 0
+ #: ID of the port to modify Tx queues on.
+ tx_port_num: ClassVar[int] = 1
+ #: Number of queues to start testpmd with. There will be the same number of Rx and Tx queues.
+ #: 8 was chosen as a number that is low enough for most NICs to accommodate while also being
+ #: enough to validate the usage of the queues.
+ number_of_queues: ClassVar[int] = 8
+ #: The number of packets to send while testing. The test calls for well over the ring size - 1
+ #: packets in the modification test case and the only options for ring size are 256 or 512,
+ #: therefore 1024 will be more than enough.
+ number_of_packets_to_send: ClassVar[int] = 1024
+
+ def send_packets_with_different_addresses(self, number_of_packets: int) -> None:
+ """Send a set number of packets each with different dst addresses.
+
+ Different destination addresses are required to ensure that each queue is used. If every
+ packet had the same address, then they would all be processed by the same queue. Note that
+ this means the current implementation of this method is limited to only work for up to 254
+ queues. A smaller subnet would be required to handle an increased number of queues.
+
+ Args:
+ number_of_packets: The number of packets to generate and then send using the traffic
+ generator.
+ """
+ packets_to_send = [
+ Ether()
+ / IP(src=self.src_addr, dst=f"{self.dst_address_subnet}.{(i % 254) + 1}")
+ / Raw()
+ for i in range(number_of_packets)
+ ]
+ self.send_packets(packets_to_send)
+
+ def port_queue_in_stats(
+ self, port_id: int, is_rx_queue: bool, queue_id: int, stats: str
+ ) -> bool:
+ """Verify if stats for a queue are in the provided output.
+
+ Args:
+ port_id: ID of the port that the queue resides on.
+ is_rx_queue: Type of queue to scan for, if :data:`True` then search for an Rx queue,
+ otherwise search for a Tx queue.
+ queue_id: ID of the queue.
+ stats: Testpmd forwarding statistics to scan for the given queue.
+
+ Returns:
+ If the queue appeared in the forwarding statistics.
+ """
+ type_of_queue = "RX" if is_rx_queue else "TX"
+ return f"{type_of_queue} Port= {port_id}/Queue={queue_id:2d}" in stats
+
+ @setup_and_teardown_test
+ def modify_ring_size(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify ring size of port queues can be configured at runtime.
+
+ Ring size of queues in `queues_to_modify` are set to 512 unless that is already their
+ configured size, in which case they are instead set to 256. Queues in `queues_to_modify`
+ are expected to already be stopped before calling this method. `testpmd` is also expected
+ to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ for queue_id in queues_to_modify:
+ curr_ring_size = testpmd.get_queue_ring_size(port_id, queue_id, is_rx_testing)
+ new_ring_size = 256 if curr_ring_size == 512 else 512
+ try:
+ testpmd.set_queue_ring_size(
+ port_id, queue_id, new_ring_size, is_rx_testing, verify=True
+ )
+ # The testpmd method verifies that the modification worked, so we catch that error
+ # and just re-raise it as a test case failure
+ except InteractiveCommandExecutionError:
+ self.verify(
+ False,
+ f"Failed to update the ring size of queue {queue_id} on port "
+ f"{port_id} at runtime",
+ )
+
+ @setup_and_teardown_test
+ def stop_queues(
+ self,
+ port_id: int,
+ queues_to_modify: MutableSet[int],
+ unchanged_queues: MutableSet[int],
+ testpmd: TestPmdShell,
+ is_rx_testing: bool,
+ ) -> None:
+ """Verify stopped queues do not handle traffic and do not block traffic on other queues.
+
+ Queues in `queues_to_modify` are expected to already be stopped before calling this method.
+ `testpmd` is also expected to already be started.
+
+ Args:
+ port_id: Port where the queues reside.
+ queues_to_modify: IDs of stopped queues to configure in the test.
+ unchanged_queues: IDs of running, unmodified queues.
+ testpmd: Running interactive testpmd application.
+ is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
+ queues will be modified.
+ """
+ testpmd.start()
+ self.send_packets_with_different_addresses(self.number_of_packets_to_send)
+ forwarding_stats = testpmd.stop()
+
+ # Checking that all unmodified queues handled some packets is important because this
+ # test case checks for the absence of stopped queues to validate that they cannot
+ # receive traffic. If there are some unchanged queues that also didn't receive traffic,
+ # it means there could be another reason for the packets not transmitting and,
+ # therefore, a false positive result.
+ for unchanged_q_id in unchanged_queues:
+ self.verify(
+ self.port_queue_in_stats(port_id, is_rx_testing, unchanged_q_id, forwarding_stats),
+ f"Queue {unchanged_q_id} failed to receive traffic.",
+ )
+ for stopped_q_id in queues_to_modify:
+ self.verify(
+ not self.port_queue_in_stats(
+ port_id, is_rx_testing, stopped_q_id, forwarding_stats
+ ),
+ f"Queue {stopped_q_id} should be stopped but still received traffic.",
+ )
+
+ @requires(NicCapability.RUNTIME_RX_QUEUE_SETUP)
+ @func_test
+ def test_rx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`True`."""
+ self.stop_queues(True)
+
+ @requires(NicCapability.RUNTIME_RX_QUEUE_SETUP)
+ @func_test
+ def test_rx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`True`."""
+ self.modify_ring_size(True)
+
+ @requires(NicCapability.RUNTIME_TX_QUEUE_SETUP)
+ @func_test
+ def test_tx_queue_stop(self):
+ """Run method for stopping queues with flag for Rx testing set to :data:`False`."""
+ self.stop_queues(False)
+
+ @requires(NicCapability.RUNTIME_TX_QUEUE_SETUP)
+ @func_test
+ def test_tx_queue_configuration(self):
+ """Run method for configuring queues with flag for Rx testing set to :data:`False`."""
+ self.modify_ring_size(False)
--
2.44.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 2/2] dts: add dynamic queue test suite
2024-11-05 16:58 ` [PATCH v6 2/2] dts: add dynamic queue test suite Dean Marx
@ 2024-11-18 23:24 ` Patrick Robb
2024-11-18 23:24 ` Patrick Robb
1 sibling, 0 replies; 81+ messages in thread
From: Patrick Robb @ 2024-11-18 23:24 UTC (permalink / raw)
To: Dean Marx
Cc: npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek, dev, Jeremy Spewock
[-- Attachment #1: Type: text/plain, Size: 26 bytes --]
Reviewed-by: Patrick Robb
[-- Attachment #2: Type: text/html, Size: 47 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 2/2] dts: add dynamic queue test suite
2024-11-05 16:58 ` [PATCH v6 2/2] dts: add dynamic queue test suite Dean Marx
2024-11-18 23:24 ` Patrick Robb
@ 2024-11-18 23:24 ` Patrick Robb
1 sibling, 0 replies; 81+ messages in thread
From: Patrick Robb @ 2024-11-18 23:24 UTC (permalink / raw)
To: Dean Marx
Cc: npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek, dev
[-- Attachment #1: Type: text/plain, Size: 29 bytes --]
Applied to next-dts, thanks.
[-- Attachment #2: Type: text/html, Size: 50 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 1/2] dts: add port queue modification and forwarding stats to testpmd
2024-11-05 16:58 ` [PATCH v6 1/2] dts: add port queue modification and forwarding stats to testpmd Dean Marx
@ 2024-11-18 23:25 ` Patrick Robb
2024-11-18 23:36 ` Patrick Robb
1 sibling, 0 replies; 81+ messages in thread
From: Patrick Robb @ 2024-11-18 23:25 UTC (permalink / raw)
To: Dean Marx
Cc: npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek, dev, Jeremy Spewock
[-- Attachment #1: Type: text/plain, Size: 46 bytes --]
Reviewed-by: Patrick Robb <probb@iol.unh.edu>
[-- Attachment #2: Type: text/html, Size: 112 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 1/2] dts: add port queue modification and forwarding stats to testpmd
2024-11-05 16:58 ` [PATCH v6 1/2] dts: add port queue modification and forwarding stats to testpmd Dean Marx
2024-11-18 23:25 ` Patrick Robb
@ 2024-11-18 23:36 ` Patrick Robb
1 sibling, 0 replies; 81+ messages in thread
From: Patrick Robb @ 2024-11-18 23:36 UTC (permalink / raw)
To: Dean Marx
Cc: npratte, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek, dev
[-- Attachment #1: Type: text/plain, Size: 399 bytes --]
Although I don't think it's necessarily wrong, I wanted to note for the
broader group that this series is indeed dropping rxq info dataclass fields
which were previously supported. It is worth discussing at the next DTS
meeting so we have better clarity on how to handle these situations going
forward. I've created a ticket: https://bugs.dpdk.org/show_bug.cgi?id=1584
Applied to next-dts, thanks.
[-- Attachment #2: Type: text/html, Size: 534 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v1 0/8] dts: move test suite imports from framework to API
2024-09-25 19:20 ` [PATCH v5 0/2] dts: add dynamic queue configuration " jspewock
` (2 preceding siblings ...)
2024-11-05 16:58 ` [PATCH v6 0/2] dts: add dynamic queue configuration " Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 17:03 ` [PATCH v1 1/8] dts: move test suite module " Dean Marx
` (8 more replies)
3 siblings, 9 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
This series moves various modules from the framework directory
to the API based on which are being imported by test suites.
These include:
- test_suite
- testbed_model
- exception
- utils
- context
- params
This eliminates all test suite framework imports except for
the remote_session imports in packet_capture, as well
as the settings/config imports in smoke_tests. I believe these
imports, and what to do with them, should be a topic of discussion
in future DTS meetings, as I don't believe they should reside in the
API, even if they are being imported in test suites.
In addition to these changes, I've split the linux_session module
into an interface in api/testbed_model/linux_session and an
implementation in framework/linux_session. This way, users
can still import linux session if necessary, without exposing
the backend implementation.
Dean Marx (8):
dts: move test suite module from framework to API
dts: move testbed model from framework to API
dts: move exception module from framework to API
dts: move utils from framework to API
dts: move context from framework to API
dts: move params directory from framework to API
dts: separate Linux session into interface and logic
dts: update API rst files for doc build
--
2.52.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v1 1/8] dts: move test suite module from framework to API
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 17:03 ` [PATCH v1 2/8] dts: move testbed model " Dean Marx
` (7 subsequent siblings)
8 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Currently, each test suite imports the TestSuite class
from the DTS framework to use as a base class.
However, the goal for 26.07 is to move all test suite
imports to the API module. Moves and updates the test_suite
file to the API directory, and updates all files that import
test_suite to reflect this change.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/api/capabilities.py | 6 +++---
dts/api/packet.py | 2 +-
dts/{framework => api}/test_suite.py | 7 +++----
dts/framework/config/__init__.py | 4 ++--
dts/framework/config/test_run.py | 8 ++++----
dts/framework/context.py | 2 +-
dts/framework/test_run.py | 2 +-
dts/framework/testbed_model/capability.py | 6 +++---
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
| 2 +-
dts/tests/TestSuite_port_control.py | 2 +-
dts/tests/TestSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 2 +-
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 2 +-
dts/tests/TestSuite_vlan.py | 2 +-
35 files changed, 45 insertions(+), 46 deletions(-)
rename dts/{framework => api}/test_suite.py (99%)
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 09bc538523..8569cacbd2 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -22,7 +22,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from framework.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -33,7 +33,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from framework.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -47,7 +47,7 @@ def test_scatter_mbuf_2048(self):
from typing import TYPE_CHECKING, Callable
if TYPE_CHECKING:
- from framework.test_suite import TestProtocol
+ from api.test_suite import TestProtocol
class LinkTopology(IntEnum):
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 094a1b7a9d..fc2be931fe 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -175,7 +175,7 @@ def adjust_addresses(packets: list[Packet], expected: bool = False) -> list[Pack
Raises:
InternalError: If no tests are running.
"""
- from framework.test_suite import TestSuite
+ from api.test_suite import TestSuite
if get_ctx().local.current_test_suite is None:
raise InternalError("No current test suite, tests aren't running?")
diff --git a/dts/framework/test_suite.py b/dts/api/test_suite.py
similarity index 99%
rename from dts/framework/test_suite.py
rename to dts/api/test_suite.py
index 69ce26040a..f107b1cd2c 100644
--- a/dts/framework/test_suite.py
+++ b/dts/api/test_suite.py
@@ -30,12 +30,11 @@
from typing_extensions import Self
from framework.config.common import FrozenModel
+from framework.exception import ConfigurationError, InternalError
+from framework.logger import DTSLogger, get_dts_logger
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
-
-from .exception import ConfigurationError, InternalError
-from .logger import DTSLogger, get_dts_logger
-from .utils import to_pascal_case
+from framework.utils import to_pascal_case
if TYPE_CHECKING:
from framework.context import Context
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index d2f0138e4a..6f4f9d82f8 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -43,7 +43,7 @@
# Import only if type checking or building docs, to prevent circular imports.
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig
+ from api.test_suite import BaseConfig
NodesConfig = Annotated[list[NodeConfiguration], Field(min_length=1)]
@@ -182,7 +182,7 @@ def load_config(ctx: ValidationContext) -> Configuration:
nodes = _load_and_parse_model(ctx["settings"].nodes_config_path, NodesConfig, ctx)
try:
- from framework.test_suite import BaseConfig as BaseConfig
+ from api.test_suite import BaseConfig as BaseConfig
Configuration.model_rebuild()
return Configuration.model_validate(
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 76e24d1785..75737418a8 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -33,7 +33,7 @@
from .common import FrozenModel, load_fields_from_settings
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
+ from api.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
@unique
@@ -230,7 +230,7 @@ class TestSuiteConfig(FrozenModel):
@cached_property
def test_suite_spec(self) -> "TestSuiteSpec":
"""The specification of the requested test suite."""
- from framework.test_suite import find_by_name
+ from api.test_suite import find_by_name
test_suite_spec = find_by_name(self.test_suite_name)
assert (
@@ -280,7 +280,7 @@ def fetch_all_test_suites() -> list[TestSuiteConfig]:
This function does not include the smoke tests.
"""
- from framework.test_suite import AVAILABLE_TEST_SUITES
+ from api.test_suite import AVAILABLE_TEST_SUITES
return [
TestSuiteConfig(test_suite=test_suite.name)
@@ -506,7 +506,7 @@ def filter_tests(
self, tests_config: dict[str, "BaseConfig"]
) -> Iterable[tuple[type["TestSuite"], "BaseConfig", deque[type["TestCase"]]]]:
"""Filter test suites and cases selected for execution."""
- from framework.test_suite import TestCaseType
+ from api.test_suite import TestCaseType
test_suites = [TestSuiteConfig(test_suite="smoke_tests")]
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 8f1021dc96..86745ab56b 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -16,8 +16,8 @@
from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
+ from api.test_suite import TestCase, TestSuite
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
- from framework.test_suite import TestCase, TestSuite
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..037ae6c9fb 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,6 +106,7 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
@@ -113,7 +114,6 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from framework.testbed_model.capability import (
Capability,
get_supported_capabilities,
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 960370fc72..39f6f3c0df 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -25,7 +25,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from framework.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -36,7 +36,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from framework.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -71,8 +71,8 @@ def test_scatter_mbuf_2048(self):
from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
+ from api.test_suite import TestCase
from api.testpmd import TestPmd
- from framework.test_suite import TestCase
P = ParamSpec("P")
TestPmdMethod = Callable[Concatenate["TestPmd", P], Any]
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index c57231de22..05bfe951fd 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,8 +11,8 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.port import Port
diff --git a/dts/tests/TestSuite_checksum_offload.py b/dts/tests/TestSuite_checksum_offload.py
index 90ca798e56..a2ea13991b 100644
--- a/dts/tests/TestSuite_checksum_offload.py
+++ b/dts/tests/TestSuite_checksum_offload.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import ChecksumOffloadOptions, PacketOffloadFlag
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_IPV4_CKSUM)
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index af0a5680ab..3f046ad87e 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -30,9 +30,9 @@
CryptodevResults,
)
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, crypto_test
from framework.context import get_ctx
from framework.exception import SkippedTestException
-from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
diff --git a/dts/tests/TestSuite_dual_vlan.py b/dts/tests/TestSuite_dual_vlan.py
index 1b77dd2b47..f3347a6d52 100644
--- a/dts/tests/TestSuite_dual_vlan.py
+++ b/dts/tests/TestSuite_dual_vlan.py
@@ -21,9 +21,9 @@
from api.capabilities import LinkTopology, requires_link_topology
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestDualVlan(TestSuite):
diff --git a/dts/tests/TestSuite_dynamic_config.py b/dts/tests/TestSuite_dynamic_config.py
index 7204ec4f73..b9e2c30da1 100644
--- a/dts/tests/TestSuite_dynamic_config.py
+++ b/dts/tests/TestSuite_dynamic_config.py
@@ -27,9 +27,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index 5ac85bee7d..e0ef1f447a 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -37,10 +37,10 @@
)
from api.packet import send_packets
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
from framework.exception import InteractiveCommandExecutionError
-from framework.test_suite import TestSuite, func_test
def setup_and_teardown_test(
diff --git a/dts/tests/TestSuite_hello_world.py b/dts/tests/TestSuite_hello_world.py
index bf1a93c782..cd62eb8f3e 100644
--- a/dts/tests/TestSuite_hello_world.py
+++ b/dts/tests/TestSuite_hello_world.py
@@ -9,8 +9,8 @@
"""
from api.test import log
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 596b892730..44fee58775 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -18,10 +18,10 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from framework.context import filter_cores
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
from framework.utils import generate_random_packets
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index a7e24b37d5..98c12459f6 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -25,9 +25,9 @@
)
from api.packet import send_packet_and_capture
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from framework.exception import InteractiveCommandExecutionError
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mtu.py b/dts/tests/TestSuite_mtu.py
index 8355495d33..c264db299e 100644
--- a/dts/tests/TestSuite_mtu.py
+++ b/dts/tests/TestSuite_mtu.py
@@ -23,8 +23,8 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
STANDARD_FRAME = 1518 # --max-pkt-len will subtract l2 information at a minimum of 18 bytes.
JUMBO_FRAME = 9018
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 4bd15e2401..d37796954d 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,11 +36,11 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreList
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py
index 96da67ee7d..6c7f6d79fe 100644
--- a/dts/tests/TestSuite_pmd_buffer_scatter.py
+++ b/dts/tests/TestSuite_pmd_buffer_scatter.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index f6adf262c3..8777d446cd 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -22,6 +22,7 @@
)
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import (
@@ -30,7 +31,6 @@
TestPmdVerbosePacket,
)
from framework.exception import InteractiveCommandExecutionError
-from framework.test_suite import BaseConfig, TestSuite, func_test
from framework.utils import StrEnum
diff --git a/dts/tests/TestSuite_port_control.py b/dts/tests/TestSuite_port_control.py
index 6be47838d0..5b960cb3a3 100644
--- a/dts/tests/TestSuite_port_control.py
+++ b/dts/tests/TestSuite_port_control.py
@@ -18,9 +18,9 @@
)
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_port_restart_config_persistency.py b/dts/tests/TestSuite_port_restart_config_persistency.py
index 4ea22b6d70..88df35d33c 100644
--- a/dts/tests/TestSuite_port_restart_config_persistency.py
+++ b/dts/tests/TestSuite_port_restart_config_persistency.py
@@ -14,9 +14,9 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import TestPmdPortFlowCtrl
-from framework.test_suite import TestSuite, func_test
ALTERNATIVE_MTU: int = 800
STANDARD_MTU: int = 1500
diff --git a/dts/tests/TestSuite_port_stats.py b/dts/tests/TestSuite_port_stats.py
index 3dc045f847..0328c6718c 100644
--- a/dts/tests/TestSuite_port_stats.py
+++ b/dts/tests/TestSuite_port_stats.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_promisc_support.py b/dts/tests/TestSuite_promisc_support.py
index a0c65dc662..c59c8c6078 100644
--- a/dts/tests/TestSuite_promisc_support.py
+++ b/dts/tests/TestSuite_promisc_support.py
@@ -21,8 +21,8 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_qinq.py b/dts/tests/TestSuite_qinq.py
index 505d71dbc8..5dde37d4db 100644
--- a/dts/tests/TestSuite_qinq.py
+++ b/dts/tests/TestSuite_qinq.py
@@ -18,8 +18,8 @@
from api.capabilities import NicCapability, requires_nic_capability
from api.packet import send_packet_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestQinq(TestSuite):
diff --git a/dts/tests/TestSuite_queue_start_stop.py b/dts/tests/TestSuite_queue_start_stop.py
index e9048d4245..6935f395c1 100644
--- a/dts/tests/TestSuite_queue_start_stop.py
+++ b/dts/tests/TestSuite_queue_start_stop.py
@@ -24,9 +24,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.TWO_LINKS)
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 6255e4c36d..3d74decb11 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -23,6 +23,7 @@
from api.capabilities import NicCapability, requires_nic_capability
from api.packet import send_packet_and_capture
from api.test import fail, log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import FlowRule
from framework.exception import (
@@ -30,7 +31,6 @@
SkippedTestException,
TestCaseVerifyError,
)
-from framework.test_suite import TestSuite, func_test
@dataclass
diff --git a/dts/tests/TestSuite_rx_tx_offload.py b/dts/tests/TestSuite_rx_tx_offload.py
index b0da627d3c..c8d24baaae 100644
--- a/dts/tests/TestSuite_rx_tx_offload.py
+++ b/dts/tests/TestSuite_rx_tx_offload.py
@@ -13,12 +13,12 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import (
OffloadConfiguration,
RxTxLiteralSwitch,
)
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.ONE_LINK)
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index 1e7ab7b036..f1eb435759 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -22,10 +22,10 @@
)
from api.packet import assess_performance_by_packet
from api.test import verify, write_performance_json
+from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
from framework.params.types import TestPmdParamsDict
-from framework.test_suite import BaseConfig, TestSuite, perf_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 271ad4301c..38ed2234cd 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,10 +19,10 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.linux_session import LinuxSession
from framework.utils import REGEX_FOR_PCI_ADDRESS
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index fa91f7ee2f..263a915745 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,9 +18,9 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.virtual_device import VirtualDevice
from framework.utils import generate_random_packets
diff --git a/dts/tests/TestSuite_uni_pkt.py b/dts/tests/TestSuite_uni_pkt.py
index 222276ce67..d83185d1b2 100644
--- a/dts/tests/TestSuite_uni_pkt.py
+++ b/dts/tests/TestSuite_uni_pkt.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
class TestUniPkt(TestSuite):
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index bdecdb76fd..fd1fc476cc 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,9 +12,9 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.capability import requires
from framework.testbed_model.linux_session import LinuxSession
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_vlan.py b/dts/tests/TestSuite_vlan.py
index 898673fc86..975e87b128 100644
--- a/dts/tests/TestSuite_vlan.py
+++ b/dts/tests/TestSuite_vlan.py
@@ -23,9 +23,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_VLAN_FILTER)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 2/8] dts: move testbed model from framework to API
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
2026-04-23 17:03 ` [PATCH v1 1/8] dts: move test suite module " Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 17:03 ` [PATCH v1 3/8] dts: move exception module " Dean Marx
` (6 subsequent siblings)
8 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites import modules from testbed model
in the framework. Move this directory to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/api/artifact.py | 2 +-
dts/api/capabilities.py | 8 ++++----
dts/api/packet.py | 14 +++++++-------
dts/api/test_suite.py | 4 ++--
dts/{framework => api}/testbed_model/__init__.py | 0
.../testbed_model/capability.py | 12 ++++++------
dts/{framework => api}/testbed_model/cpu.py | 4 ++--
.../testbed_model/linux_session.py | 6 +++---
dts/{framework => api}/testbed_model/node.py | 2 +-
.../testbed_model/os_session.py | 8 ++++----
dts/{framework => api}/testbed_model/port.py | 2 +-
.../testbed_model/posix_session.py | 6 +++---
dts/{framework => api}/testbed_model/topology.py | 6 +++---
.../testbed_model/traffic_generator/__init__.py | 4 ++--
.../capturing_traffic_generator.py | 4 ++--
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 14 +++++++-------
.../traffic_generator/traffic_generator.py | 8 ++++----
.../testbed_model/traffic_generator/trex.py | 16 ++++++++--------
.../testbed_model/virtual_device.py | 0
dts/framework/config/node.py | 16 ++++++++--------
dts/framework/config/test_run.py | 2 +-
dts/framework/context.py | 10 +++++-----
dts/framework/params/eal.py | 6 +++---
dts/framework/params/types.py | 6 +++---
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 8 ++++----
dts/framework/remote_session/dpdk_shell.py | 2 +-
.../remote_session/interactive_shell.py | 2 +-
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 2 +-
dts/framework/test_run.py | 16 ++++++++--------
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 8 ++++----
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 6 +++---
39 files changed, 109 insertions(+), 109 deletions(-)
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (98%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
rename dts/{framework => api}/testbed_model/linux_session.py (99%)
rename dts/{framework => api}/testbed_model/node.py (99%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (98%)
rename dts/{framework => api}/testbed_model/posix_session.py (99%)
rename dts/{framework => api}/testbed_model/topology.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (95%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (91%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (96%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 24a2b05063..025c87bbfa 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -47,10 +47,10 @@
from paramiko import SFTPClient, SFTPFile
from typing_extensions import Buffer
+from api.testbed_model.node import Node, NodeIdentifier, get_node
from framework.exception import InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node, NodeIdentifier, get_node
TextMode: TypeAlias = (
Literal["r", "r+", "w", "w+", "a", "a+", "x", "x+"]
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 8569cacbd2..a4d6b2b424 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -23,7 +23,7 @@
.. code:: python
from api.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires_link_topology
+ from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -34,7 +34,7 @@ def hello_world_single_core(self):
.. code:: python
from api.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires_nic_capability
+ from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -235,7 +235,7 @@ def requires_link_topology(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import TopologyCapability
+ from api.testbed_model.capability import TopologyCapability
def add_required_topology(
test_case_or_suite: type["TestProtocol"],
@@ -258,7 +258,7 @@ def requires_nic_capability(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import DecoratedNicCapability
+ from api.testbed_model.capability import DecoratedNicCapability
def add_required_capability(
test_case_or_suite: type["TestProtocol"],
diff --git a/dts/api/packet.py b/dts/api/packet.py
index fc2be931fe..d30d455485 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -28,14 +28,14 @@
from scapy.packet import Packet, Padding, raw
from api.test import fail, log_debug
-from framework.context import get_ctx
-from framework.exception import InternalError
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
+from framework.context import get_ctx
+from framework.exception import InternalError
from framework.utils import get_packet_summaries
@@ -82,10 +82,10 @@ def send_packets_and_capture(
Returns:
A list of received packets.
"""
- from framework.context import get_ctx
- from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+ from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
+ from framework.context import get_ctx
assert isinstance(
get_ctx().func_tg, CapturingTrafficGenerator
@@ -340,7 +340,7 @@ def assess_performance_by_packet(
Returns:
Performance statistics of the generated test.
"""
- from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+ from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficGenerator,
)
diff --git a/dts/api/test_suite.py b/dts/api/test_suite.py
index f107b1cd2c..ebb07a9cae 100644
--- a/dts/api/test_suite.py
+++ b/dts/api/test_suite.py
@@ -29,11 +29,11 @@
from scapy.packet import Packet
from typing_extensions import Self
+from api.testbed_model.capability import TestProtocol
+from api.testbed_model.topology import Topology
from framework.config.common import FrozenModel
from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
-from framework.testbed_model.capability import TestProtocol
-from framework.testbed_model.topology import Topology
from framework.utils import to_pascal_case
if TYPE_CHECKING:
diff --git a/dts/framework/testbed_model/__init__.py b/dts/api/testbed_model/__init__.py
similarity index 100%
rename from dts/framework/testbed_model/__init__.py
rename to dts/api/testbed_model/__init__.py
diff --git a/dts/framework/testbed_model/capability.py b/dts/api/testbed_model/capability.py
similarity index 98%
rename from dts/framework/testbed_model/capability.py
rename to dts/api/testbed_model/capability.py
index 39f6f3c0df..95583261d8 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -26,7 +26,7 @@
.. code:: python
from api.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires
+ from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -37,7 +37,7 @@ def hello_world_single_core(self):
.. code:: python
from api.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires
+ from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -64,11 +64,11 @@ def test_scatter_mbuf_2048(self):
from typing_extensions import Self
from api.capabilities import LinkTopology, NicCapability
-from framework.exception import ConfigurationError, InternalError, SkippedTestException
+from api.exception import ConfigurationError, InternalError, SkippedTestException
+from api.testbed_model.node import Node
+from api.testbed_model.port import DriverKind
+from api.testbed_model.topology import Topology
from framework.logger import get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import DriverKind
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
from api.test_suite import TestCase
diff --git a/dts/framework/testbed_model/cpu.py b/dts/api/testbed_model/cpu.py
similarity index 99%
rename from dts/framework/testbed_model/cpu.py
rename to dts/api/testbed_model/cpu.py
index 6e2ecca080..ee754f5844 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/api/testbed_model/cpu.py
@@ -24,12 +24,12 @@
from dataclasses import dataclass
from enum import auto, unique
-from framework.utils import StrEnum, expand_range
+from api.utils import StrEnum, expand_range
@unique
class Architecture(StrEnum):
- r"""The supported architectures of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported architectures of :class:`~api.testbed_model.node.Node`\s."""
#:
i686 = auto()
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
similarity index 99%
rename from dts/framework/testbed_model/linux_session.py
rename to dts/api/testbed_model/linux_session.py
index ee943462c2..7307b2abe2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -18,13 +18,13 @@
from typing_extensions import NotRequired
-from framework.exception import (
+from api.exception import (
ConfigurationError,
InternalError,
RemoteCommandExecutionError,
)
-from framework.testbed_model.port import PortInfo
-from framework.utils import expand_range
+from api.testbed_model.port import PortInfo
+from api.utils import expand_range
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/node.py b/dts/api/testbed_model/node.py
similarity index 99%
rename from dts/framework/testbed_model/node.py
rename to dts/api/testbed_model/node.py
index 67a96ef4e5..4f42bf6aeb 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -17,11 +17,11 @@
from pathlib import PurePath
from typing import Literal, TypeAlias
+from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
-from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
diff --git a/dts/framework/testbed_model/os_session.py b/dts/api/testbed_model/os_session.py
similarity index 99%
rename from dts/framework/testbed_model/os_session.py
rename to dts/api/testbed_model/os_session.py
index 2c267afed1..b1e0538ac9 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/api/testbed_model/os_session.py
@@ -29,12 +29,12 @@
from enum import Flag, auto
from pathlib import Path, PurePath, PurePosixPath
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.remote_session.interactive_remote_session import InteractiveRemoteSession
from framework.remote_session.remote_session import CommandResult, RemoteSession
from framework.settings import SETTINGS
-from framework.utils import MesonArgs, TarCompressionFormat
from .cpu import Architecture, LogicalCore
from .port import Port, PortInfo
@@ -73,11 +73,11 @@ class OSSessionInfo:
Attributes:
os_name: The name of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
os_version: The version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
kernel_version: The kernel version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
"""
os_name: str
diff --git a/dts/framework/testbed_model/port.py b/dts/api/testbed_model/port.py
similarity index 98%
rename from dts/framework/testbed_model/port.py
rename to dts/api/testbed_model/port.py
index d81bc4cda0..aea3e59c25 100644
--- a/dts/framework/testbed_model/port.py
+++ b/dts/api/testbed_model/port.py
@@ -12,8 +12,8 @@
from functools import cached_property
from typing import TYPE_CHECKING, Any, Final, Literal, NamedTuple
+from api.exception import InternalError
from framework.config.node import PortConfig
-from framework.exception import InternalError
if TYPE_CHECKING:
from .node import Node
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/api/testbed_model/posix_session.py
similarity index 99%
rename from dts/framework/testbed_model/posix_session.py
rename to dts/api/testbed_model/posix_session.py
index dec952685a..61c634dad1 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/api/testbed_model/posix_session.py
@@ -16,15 +16,15 @@
from collections.abc import Iterable
from pathlib import Path, PurePath, PurePosixPath
-from framework.exception import DPDKBuildError, RemoteCommandExecutionError
-from framework.settings import SETTINGS
-from framework.utils import (
+from api.exception import DPDKBuildError, RemoteCommandExecutionError
+from api.utils import (
MesonArgs,
TarCompressionFormat,
convert_to_list_of_string,
create_tarball,
extract_tarball,
)
+from framework.settings import SETTINGS
from .cpu import Architecture
from .os_session import FilePermissions, OSSession, OSSessionInfo
diff --git a/dts/framework/testbed_model/topology.py b/dts/api/testbed_model/topology.py
similarity index 98%
rename from dts/framework/testbed_model/topology.py
rename to dts/api/testbed_model/topology.py
index 34862c4d2e..5b8fe03836 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/api/testbed_model/topology.py
@@ -18,9 +18,9 @@
from typing_extensions import Self
from api.capabilities import LinkTopology
-from framework.exception import ConfigurationError, InternalError
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.node import Node, NodeIdentifier
+from api.exception import ConfigurationError, InternalError
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.node import Node, NodeIdentifier
from .port import DriverKind, Port, PortConfig
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/api/testbed_model/traffic_generator/__init__.py
similarity index 95%
rename from dts/framework/testbed_model/traffic_generator/__init__.py
rename to dts/api/testbed_model/traffic_generator/__init__.py
index fca251f534..11fa25448a 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/api/testbed_model/traffic_generator/__init__.py
@@ -14,13 +14,13 @@
and a capturing traffic generator is required.
"""
+from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.exception import ConfigurationError
-from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
similarity index 98%
rename from dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
index 7655751d7e..db274e5e82 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
-from framework.testbed_model.port import Port
-from framework.utils import get_packet_summaries
+from api.testbed_model.port import Port
+from api.utils import get_packet_summaries
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py b/dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
similarity index 100%
rename from dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/api/testbed_model/traffic_generator/scapy.py
similarity index 98%
rename from dts/framework/testbed_model/traffic_generator/scapy.py
rename to dts/api/testbed_model/traffic_generator/scapy.py
index c6e9006205..215c57f93d 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/api/testbed_model/traffic_generator/scapy.py
@@ -25,16 +25,16 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet
+from api.exception import InteractiveSSHSessionDeadError, InternalError
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
-from framework.exception import InteractiveSSHSessionDeadError, InternalError
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
from .capturing_traffic_generator import CapturingTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/api/testbed_model/traffic_generator/traffic_generator.py
similarity index 91%
rename from dts/framework/testbed_model/traffic_generator/traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/traffic_generator.py
index cdda5a7c08..5fd68e5144 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/traffic_generator.py
@@ -13,11 +13,11 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
from framework.config.test_run import TrafficGeneratorConfig
from framework.logger import DTSLogger, get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
class TrafficGenerator(ABC):
@@ -25,7 +25,7 @@ class TrafficGenerator(ABC):
Exposes the common public methods of all traffic generators and defines private methods
that must implement the traffic generation logic in subclasses. This class also extends from
- :class:`framework.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
+ :class:`api.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
from multiple classes to fulfil the traffic generating functionality without breaking
single inheritance.
"""
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/api/testbed_model/traffic_generator/trex.py
similarity index 96%
rename from dts/framework/testbed_model/traffic_generator/trex.py
rename to dts/api/testbed_model/traffic_generator/trex.py
index 22cd20dea9..d97ed934c9 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/api/testbed_model/traffic_generator/trex.py
@@ -11,19 +11,19 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node, create_session
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
+ PerformanceTrafficGenerator,
+ PerformanceTrafficStats,
+)
+from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node, create_session
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
- PerformanceTrafficGenerator,
- PerformanceTrafficStats,
-)
-from framework.utils import StrEnum
@dataclass(slots=True)
diff --git a/dts/framework/testbed_model/virtual_device.py b/dts/api/testbed_model/virtual_device.py
similarity index 100%
rename from dts/framework/testbed_model/virtual_device.py
rename to dts/api/testbed_model/virtual_device.py
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 792290f11f..63062a31b5 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -21,7 +21,7 @@
@unique
class OS(StrEnum):
- r"""The supported operating systems of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported operating systems of :class:`~api.testbed_model.node.Node`\s."""
#:
linux = auto()
@@ -32,7 +32,7 @@ class OS(StrEnum):
class HugepageConfiguration(FrozenModel):
- r"""The hugepage configuration of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The hugepage configuration of :class:`~api.testbed_model.node.Node`\s."""
#: The number of hugepages to allocate.
number_of: int
@@ -41,7 +41,7 @@ class HugepageConfiguration(FrozenModel):
class PortConfig(FrozenModel):
- r"""The port configuration of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The port configuration of :class:`~api.testbed_model.node.Node`\s."""
#: An identifier for the port. May contain letters, digits, underscores, hyphens and spaces.
name: str = Field(pattern=REGEX_FOR_IDENTIFIER)
@@ -54,17 +54,17 @@ class PortConfig(FrozenModel):
class NodeConfiguration(FrozenModel):
- r"""The configuration of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The configuration of :class:`~api.testbed_model.node.Node`\s."""
- #: The name of the :class:`~framework.testbed_model.node.Node`.
+ #: The name of the :class:`~api.testbed_model.node.Node`.
name: str = Field(pattern=REGEX_FOR_IDENTIFIER)
- #: The hostname of the :class:`~framework.testbed_model.node.Node`. Can also be an IP address.
+ #: The hostname of the :class:`~api.testbed_model.node.Node`. Can also be an IP address.
hostname: str
- #: The name of the user used to connect to the :class:`~framework.testbed_model.node.Node`.
+ #: The name of the user used to connect to the :class:`~api.testbed_model.node.Node`.
user: str
#: The password of the user. The use of passwords is heavily discouraged, please use SSH keys.
password: str | None = None
- #: The operating system of the :class:`~framework.testbed_model.node.Node`.
+ #: The operating system of the :class:`~api.testbed_model.node.Node`.
os: OS
#: An optional hugepage configuration.
hugepages: HugepageConfiguration | None = Field(None, alias="hugepages_2mb")
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 75737418a8..2c16a712eb 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -38,7 +38,7 @@
@unique
class Compiler(StrEnum):
- r"""The supported compilers of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported compilers of :class:`~api.testbed_model.node.Node`\s."""
#:
gcc = auto()
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 86745ab56b..618eb3dda7 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -8,18 +8,18 @@
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
+from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
+from api.testbed_model.node import Node
+from api.testbed_model.topology import Topology
from framework.exception import InternalError
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
-from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
from api.test_suite import TestCase, TestSuite
+ from api.testbed_model.capability import TestProtocol
+ from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
- from framework.testbed_model.capability import TestProtocol
- from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
P = ParamSpec("P")
diff --git a/dts/framework/params/eal.py b/dts/framework/params/eal.py
index e84a20f02f..86bfd3fcc6 100644
--- a/dts/framework/params/eal.py
+++ b/dts/framework/params/eal.py
@@ -6,12 +6,12 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.virtual_device import VirtualDevice
from framework.params import Params, Switch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.virtual_device import VirtualDevice
if TYPE_CHECKING:
- from framework.testbed_model.port import Port
+ from api.testbed_model.port import Port
def _port_to_pci(port: "Port") -> str:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 3c7650474c..f2fa69f8b8 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -32,6 +32,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.port import Port
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -54,9 +57,6 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TxUDPPortPair,
)
from framework.params import Switch, YesNoSwitch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.port import Port
-from framework.testbed_model.virtual_device import VirtualDevice
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index c3b02dcc62..4181c20e43 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -30,12 +30,12 @@
from typing_extensions import Self
+from api.testbed_model.node import Node
from framework.context import get_ctx
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
-from framework.testbed_model.node import Node
P = TypeVar("P", bound=Params)
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index c3575cfcaf..c955f4def2 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,6 +13,10 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
+from api.testbed_model.node import Node
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.virtual_device import VirtualDevice
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -29,10 +33,6 @@
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
-from framework.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
-from framework.testbed_model.node import Node
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.virtual_device import VirtualDevice
from framework.utils import MesonArgs, TarCompressionFormat
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 269c2cada4..ac89ec0459 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -10,13 +10,13 @@
from abc import ABC, abstractmethod
from pathlib import PurePath
+from api.testbed_model.cpu import LogicalCoreList
from framework.context import get_ctx
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
)
-from framework.testbed_model.cpu import LogicalCoreList
def compute_eal_params(
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index a65cbce209..23d05fbdff 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,6 +29,7 @@
from paramiko import Channel, channel
from typing_extensions import Self
+from api.testbed_model.node import Node
from framework.context import get_ctx
from framework.exception import (
InteractiveCommandExecutionError,
@@ -38,7 +39,6 @@
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node
P = ParamSpec("P")
T = TypeVar("T", bound="InteractiveShell")
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index 6ea4749ff4..0e245a515b 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -12,10 +12,10 @@
import sys
import textwrap
+from api.testbed_model.node import Node
from framework.config.common import ValidationContext
from framework.exception import ConfigurationError
from framework.test_run import TestRun
-from framework.testbed_model.node import Node
from .config import Configuration, load_config
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 21faa55dc1..e2efff8681 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -35,9 +35,9 @@
)
from typing_extensions import OrderedDict
+from api.testbed_model.os_session import OSSessionInfo
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
-from framework.testbed_model.os_session import OSSessionInfo
from .exception import DTSError, ErrorSeverity, InternalError
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 037ae6c9fb..14c04d1ce0 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -107,6 +107,14 @@
from typing import ClassVar, Protocol, Union
from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
+from api.testbed_model.capability import (
+ Capability,
+ get_supported_capabilities,
+ test_if_supported,
+)
+from api.testbed_model.node import Node
+from api.testbed_model.topology import PortLink, Topology
+from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
@@ -114,14 +122,6 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.testbed_model.capability import (
- Capability,
- get_supported_capabilities,
- test_if_supported,
-)
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import PortLink, Topology
-from framework.testbed_model.traffic_generator import create_traffic_generator
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index 05bfe951fd..31e69c0de9 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -12,8 +12,8 @@
)
from api.test import verify
from api.test_suite import TestSuite, func_test
+from api.testbed_model.port import Port
from api.testpmd import TestPmd
-from framework.testbed_model.port import Port
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 3f046ad87e..83ce94d4df 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -31,9 +31,9 @@
)
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, crypto_test
+from api.testbed_model.virtual_device import VirtualDevice
from framework.context import get_ctx
from framework.exception import SkippedTestException
-from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 44fee58775..63f771d594 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -19,10 +19,10 @@
send_packets_and_capture,
)
from api.test_suite import TestSuite, func_test
+from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from framework.context import filter_cores
-from framework.testbed_model.cpu import LogicalCoreCount
from framework.utils import generate_random_packets
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index d37796954d..fd5cef5268 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -37,14 +37,14 @@
)
from api.test import verify
from api.test_suite import TestSuite, func_test
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from api.testpmd import TestPmd
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 38ed2234cd..157dec7585 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -20,10 +20,10 @@
)
from api.test import verify
from api.test_suite import TestSuite, func_test
+from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.testbed_model.linux_session import LinuxSession
from framework.utils import REGEX_FOR_PCI_ADDRESS
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index 263a915745..0696933053 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -19,9 +19,9 @@
send_packets_and_capture,
)
from api.test_suite import TestSuite, func_test
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
-from framework.testbed_model.virtual_device import VirtualDevice
from framework.utils import generate_random_packets
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index fd1fc476cc..2c10478df3 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -13,11 +13,11 @@
from api.packet import send_packets_and_capture
from api.test import log, verify
from api.test_suite import TestSuite, func_test
+from api.testbed_model.capability import requires
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.testbed_model.capability import requires
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.virtual_device import VirtualDevice
class TestVirtioFwd(TestSuite):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 3/8] dts: move exception module from framework to API
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
2026-04-23 17:03 ` [PATCH v1 1/8] dts: move test suite module " Dean Marx
2026-04-23 17:03 ` [PATCH v1 2/8] dts: move testbed model " Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 17:03 ` [PATCH v1 4/8] dts: move utils " Dean Marx
` (5 subsequent siblings)
8 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites currently import the exception module
from the framework in order to catch certain errors during
test execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/api/artifact.py | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 2 +-
dts/api/test.py | 2 +-
dts/api/test_suite.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/config/__init__.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/context.py | 2 +-
dts/framework/parser.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
.../remote_session/interactive_remote_session.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 6 +++---
dts/framework/remote_session/remote_session.py | 4 ++--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 3 +--
dts/framework/test_run.py | 4 ++--
dts/framework/utils.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
| 2 +-
dts/tests/TestSuite_rte_flow.py | 10 +++++-----
24 files changed, 31 insertions(+), 32 deletions(-)
rename dts/{framework => api}/exception.py (100%)
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 025c87bbfa..02d807241f 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -47,8 +47,8 @@
from paramiko import SFTPClient, SFTPFile
from typing_extensions import Buffer
+from api.exception import InternalError
from api.testbed_model.node import Node, NodeIdentifier, get_node
-from framework.exception import InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index a4fafc3713..c6a220dced 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -22,8 +22,8 @@
ThroughputResults,
VerifyResults,
)
+from api.exception import RemoteCommandExecutionError, SkippedTestException
from framework.context import get_ctx
-from framework.exception import RemoteCommandExecutionError, SkippedTestException
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/framework/exception.py b/dts/api/exception.py
similarity index 100%
rename from dts/framework/exception.py
rename to dts/api/exception.py
diff --git a/dts/api/packet.py b/dts/api/packet.py
index d30d455485..2bf31aa753 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,6 +27,7 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.exception import InternalError
from api.test import fail, log_debug
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -35,7 +36,6 @@
PerformanceTrafficStats,
)
from framework.context import get_ctx
-from framework.exception import InternalError
from framework.utils import get_packet_summaries
diff --git a/dts/api/test.py b/dts/api/test.py
index e17babe0ca..9cad9a9495 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.context import get_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/api/test_suite.py b/dts/api/test_suite.py
index ebb07a9cae..be13485f9b 100644
--- a/dts/api/test_suite.py
+++ b/dts/api/test_suite.py
@@ -29,10 +29,10 @@
from scapy.packet import Packet
from typing_extensions import Self
+from api.exception import ConfigurationError, InternalError
from api.testbed_model.capability import TestProtocol
from api.testbed_model.topology import Topology
from framework.config.common import FrozenModel
-from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.utils import to_pascal_case
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..9498d723d5 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
@@ -55,7 +56,6 @@
VLANOffloadFlag,
)
from framework.context import get_ctx
-from framework.exception import InteractiveCommandExecutionError, InternalError
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index 6f4f9d82f8..3a3580aaf7 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -35,7 +35,7 @@
from pydantic import Field, TypeAdapter, ValidationError, model_validator
from typing_extensions import Self
-from framework.exception import ConfigurationError
+from api.exception import ConfigurationError
from .common import FrozenModel, ValidationContext
from .node import NodeConfiguration
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 2c16a712eb..1b051fbadf 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -27,7 +27,7 @@
)
from typing_extensions import TYPE_CHECKING, Self
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 618eb3dda7..7ed4cc5665 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -8,10 +8,10 @@
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
+from api.exception import InternalError
from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
from api.testbed_model.node import Node
from api.testbed_model.topology import Topology
-from framework.exception import InternalError
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 3075c36857..ebf470ad30 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -15,7 +15,7 @@
from typing_extensions import Self
-from framework.exception import InternalError
+from api.exception import InternalError
class ParserFn(TypedDict):
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index c955f4def2..91173e0796 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,6 +13,7 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.exception import ConfigurationError, RemoteFileNotFoundError
from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
@@ -29,7 +30,6 @@
RemoteDPDKTreeLocation,
)
from framework.context import get_ctx
-from framework.exception import ConfigurationError, RemoteFileNotFoundError
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/interactive_remote_session.py b/dts/framework/remote_session/interactive_remote_session.py
index c8156b4345..04f45e0df8 100644
--- a/dts/framework/remote_session/interactive_remote_session.py
+++ b/dts/framework/remote_session/interactive_remote_session.py
@@ -15,8 +15,8 @@
SSHException,
)
+from api.exception import SSHConnectionError
from framework.config.node import NodeConfiguration
-from framework.exception import SSHConnectionError
from framework.logger import DTSLogger
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index 23d05fbdff..15743949e7 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,13 +29,13 @@
from paramiko import Channel, channel
from typing_extensions import Self
-from api.testbed_model.node import Node
-from framework.context import get_ctx
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from api.testbed_model.node import Node
+from framework.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py
index 158325bb7f..f49966070f 100644
--- a/dts/framework/remote_session/remote_session.py
+++ b/dts/framework/remote_session/remote_session.py
@@ -24,13 +24,13 @@
SSHException,
)
-from framework.config.node import NodeConfiguration
-from framework.exception import (
+from api.exception import (
RemoteCommandExecutionError,
SSHConnectionError,
SSHSessionDeadError,
SSHTimeoutError,
)
+from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.settings import SETTINGS
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index 0e245a515b..29be7b80fe 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -12,9 +12,9 @@
import sys
import textwrap
+from api.exception import ConfigurationError
from api.testbed_model.node import Node
from framework.config.common import ValidationContext
-from framework.exception import ConfigurationError
from framework.test_run import TestRun
from .config import Configuration, load_config
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index e2efff8681..5f945163ce 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -35,12 +35,11 @@
)
from typing_extensions import OrderedDict
+from api.exception import DTSError, ErrorSeverity, InternalError
from api.testbed_model.os_session import OSSessionInfo
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
-from .exception import DTSError, ErrorSeverity, InternalError
-
class Result(IntEnum):
"""The possible states that a setup, a teardown or a test case may end up in."""
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 14c04d1ce0..9972d26b04 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,6 +106,7 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from api.testbed_model.capability import (
Capability,
@@ -117,7 +118,6 @@
from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
@@ -136,7 +136,7 @@ class TestRun:
If an error occurs, the current stage is aborted, the error is recorded, everything in
the inner stages is marked as blocked and the run continues in the next iteration
of the same stage. The return code is the highest `severity` of all
- :class:`~.framework.exception.DTSError`\s.
+ :class:`~.api.exception.DTSError`\s.
Example:
An error occurs in a test suite setup. The current test suite is aborted,
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 9917ffbfaa..28e344871a 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -26,7 +26,7 @@
from scapy.layers.inet import IP, TCP, UDP, Ether
from scapy.packet import Packet
-from .exception import InternalError
+from api.exception import InternalError
REGEX_FOR_PCI_ADDRESS: str = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}"
_REGEX_FOR_COLON_OR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}[:-]){5}[\da-fA-F]{2}"
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 83ce94d4df..fb6fda3bac 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -29,11 +29,11 @@
from api.cryptodev.types import (
CryptodevResults,
)
+from api.exception import SkippedTestException
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
from framework.context import get_ctx
-from framework.exception import SkippedTestException
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index e0ef1f447a..24584c7d60 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -35,12 +35,12 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.exception import InteractiveCommandExecutionError
def setup_and_teardown_test(
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index 98c12459f6..eb1413f336 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -23,11 +23,11 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.exception import InteractiveCommandExecutionError
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 8777d446cd..dae90ee2d5 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -20,6 +20,7 @@
requires_link_topology,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, func_test
@@ -30,7 +31,6 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
-from framework.exception import InteractiveCommandExecutionError
from framework.utils import StrEnum
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 3d74decb11..8c5c59edec 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -21,16 +21,16 @@
from scapy.packet import Packet, Raw
from api.capabilities import NicCapability, requires_nic_capability
+from api.exception import (
+ InteractiveCommandExecutionError,
+ SkippedTestException,
+ TestCaseVerifyError,
+)
from api.packet import send_packet_and_capture
from api.test import fail, log, verify
from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import FlowRule
-from framework.exception import (
- InteractiveCommandExecutionError,
- SkippedTestException,
- TestCaseVerifyError,
-)
@dataclass
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 4/8] dts: move utils from framework to API
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
` (2 preceding siblings ...)
2026-04-23 17:03 ` [PATCH v1 3/8] dts: move exception module " Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 17:03 ` [PATCH v1 5/8] dts: move context " Dean Marx
` (4 subsequent siblings)
8 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The utils module is used to generate a set of random
packets in certain test suites. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/api/cryptodev/config.py | 2 +-
dts/api/packet.py | 2 +-
dts/api/test_suite.py | 2 +-
dts/api/testpmd/config.py | 2 +-
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 0
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
| 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
13 files changed, 12 insertions(+), 12 deletions(-)
rename dts/{framework => api}/utils.py (100%)
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index 69ff7aa59a..a88e70d45c 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.utils import StrEnum
from framework.params import Params, Switch
from framework.params.eal import EalParams
-from framework.utils import StrEnum
Silent = Literal[""]
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 2bf31aa753..2be00ad48a 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -35,8 +35,8 @@
from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
+from api.utils import get_packet_summaries
from framework.context import get_ctx
-from framework.utils import get_packet_summaries
def send_packet_and_capture(
diff --git a/dts/api/test_suite.py b/dts/api/test_suite.py
index be13485f9b..7feb35a9f8 100644
--- a/dts/api/test_suite.py
+++ b/dts/api/test_suite.py
@@ -32,9 +32,9 @@
from api.exception import ConfigurationError, InternalError
from api.testbed_model.capability import TestProtocol
from api.testbed_model.topology import Topology
+from api.utils import to_pascal_case
from framework.config.common import FrozenModel
from framework.logger import DTSLogger, get_dts_logger
-from framework.utils import to_pascal_case
if TYPE_CHECKING:
from framework.context import Context
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index e71a3e1ef0..8b688834ee 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,6 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
+from api.utils import StrEnum
from framework.params import (
Params,
Switch,
@@ -24,7 +25,6 @@
str_from_flag_value,
)
from framework.params.eal import EalParams
-from framework.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..5c847b4bd6 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -15,8 +15,8 @@
from typing_extensions import Self
+from api.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
from framework.parser import ParserFn, TextParser
-from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
RxTxLiteralSwitch = Literal["rx", "tx"]
diff --git a/dts/framework/utils.py b/dts/api/utils.py
similarity index 100%
rename from dts/framework/utils.py
rename to dts/api/utils.py
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 63062a31b5..d7122d13d8 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -14,7 +14,7 @@
from pydantic import Field, model_validator
from typing_extensions import Self
-from framework.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
+from api.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
from .common import FrozenModel
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 1b051fbadf..10901c740d 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -28,7 +28,7 @@
from typing_extensions import TYPE_CHECKING, Self
from api.exception import InternalError
-from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
+from api.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 91173e0796..1a4fec8ec9 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -18,6 +18,7 @@
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
from api.testbed_model.virtual_device import VirtualDevice
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -33,7 +34,6 @@
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
-from framework.utils import MesonArgs, TarCompressionFormat
@dataclass(slots=True, frozen=True)
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 63f771d594..0b0b7bc931 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -22,8 +22,8 @@
from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
+from api.utils import generate_random_packets
from framework.context import filter_cores
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index dae90ee2d5..162e08ccbc 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -31,7 +31,7 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
-from framework.utils import StrEnum
+from api.utils import StrEnum
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 157dec7585..b3eb325fc0 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -22,9 +22,9 @@
from api.test_suite import TestSuite, func_test
from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
+from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.utils import REGEX_FOR_PCI_ADDRESS
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index 0696933053..05a6d3aa18 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -22,7 +22,7 @@
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
-from framework.utils import generate_random_packets
+from api.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 5/8] dts: move context from framework to API
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
` (3 preceding siblings ...)
2026-04-23 17:03 ` [PATCH v1 4/8] dts: move utils " Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 17:03 ` [PATCH v1 6/8] dts: move params directory " Dean Marx
` (3 subsequent siblings)
8 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
A couple test suites import and get the run context
during execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/api/artifact.py | 2 +-
dts/{framework => api}/context.py | 0
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 4 ++--
dts/api/test.py | 2 +-
dts/api/test_suite.py | 4 ++--
dts/api/testbed_model/node.py | 2 +-
dts/api/testbed_model/topology.py | 14 +++++++-------
dts/api/testpmd/__init__.py | 2 +-
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/framework/test_run.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
16 files changed, 23 insertions(+), 23 deletions(-)
rename dts/{framework => api}/context.py (100%)
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 02d807241f..74a8ac667f 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -86,7 +86,7 @@ def make_file_path(
path /= custom_path
else:
- from framework.context import get_ctx
+ from api.context import get_ctx
try:
ctx = get_ctx()
diff --git a/dts/framework/context.py b/dts/api/context.py
similarity index 100%
rename from dts/framework/context.py
rename to dts/api/context.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index c6a220dced..67dcb02130 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -23,7 +23,7 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 2be00ad48a..393fee542b 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -36,7 +36,7 @@
PerformanceTrafficStats,
)
from api.utils import get_packet_summaries
-from framework.context import get_ctx
+from api.context import get_ctx
def send_packet_and_capture(
@@ -85,7 +85,7 @@ def send_packets_and_capture(
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
- from framework.context import get_ctx
+ from api.context import get_ctx
assert isinstance(
get_ctx().func_tg, CapturingTrafficGenerator
diff --git a/dts/api/test.py b/dts/api/test.py
index 9cad9a9495..a1f2326075 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -11,7 +11,7 @@
from api.artifact import Artifact
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger
diff --git a/dts/api/test_suite.py b/dts/api/test_suite.py
index 7feb35a9f8..0822f9bfe5 100644
--- a/dts/api/test_suite.py
+++ b/dts/api/test_suite.py
@@ -37,7 +37,7 @@
from framework.logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
- from framework.context import Context
+ from api.context import Context
class BaseConfig(FrozenModel):
@@ -91,7 +91,7 @@ def __init__(self, config: BaseConfig) -> None:
Args:
config: The test suite configuration.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self.config = config
self._ctx = get_ctx()
diff --git a/dts/api/testbed_model/node.py b/dts/api/testbed_model/node.py
index 4f42bf6aeb..40dd7f0666 100644
--- a/dts/api/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -242,7 +242,7 @@ def get_node(node_identifier: NodeIdentifier) -> Node | None:
if node_identifier == "local":
return None
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
if node_identifier == "sut":
diff --git a/dts/api/testbed_model/topology.py b/dts/api/testbed_model/topology.py
index 5b8fe03836..11593d64d5 100644
--- a/dts/api/testbed_model/topology.py
+++ b/dts/api/testbed_model/topology.py
@@ -96,7 +96,7 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
Raises:
InternalError: If the given `node_identifier` is invalid.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
match node_identifier:
@@ -180,7 +180,7 @@ def instantiate_crypto_ports(self) -> None:
Raises:
InternalError: If crypto virtual functions could not be created on a port.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
for port in ctx.sut_node.cryptodevs:
@@ -206,7 +206,7 @@ def instantiate_vf_ports(self) -> None:
Raises:
InternalError: If virtual function creation fails.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -235,7 +235,7 @@ def instantiate_vf_ports(self) -> None:
def delete_vf_ports(self) -> None:
"""Delete virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -246,7 +246,7 @@ def delete_vf_ports(self) -> None:
def delete_crypto_vf_ports(self) -> None:
"""Delete crypto virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -259,7 +259,7 @@ def bind_cryptodevs(self, driver: DriverKind):
Args:
driver: The driver to bind the crypto functions
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
@@ -318,7 +318,7 @@ def _prepare_devbind_script(self) -> None:
Raises:
InternalError: If dpdk-devbind.py could not be found.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
local_script_path = Path("..", "usertools", "dpdk-devbind.py").resolve()
valid_script_path = local_script_path.exists()
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9498d723d5..a528663c21 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -55,7 +55,7 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 4181c20e43..955a7ccdba 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,7 +31,7 @@
from typing_extensions import Self
from api.testbed_model.node import Node
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 1a4fec8ec9..68897bfefe 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -30,7 +30,7 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index ac89ec0459..947f60f75c 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,7 +11,7 @@
from pathlib import PurePath
from api.testbed_model.cpu import LogicalCoreList
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index 15743949e7..6aebef3f45 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -35,7 +35,7 @@
InteractiveSSHTimeoutError,
)
from api.testbed_model.node import Node
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 9972d26b04..914bf9491d 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -117,7 +117,7 @@
from api.testbed_model.topology import PortLink, Topology
from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
-from framework.context import Context, init_ctx
+from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index fb6fda3bac..67ebbc64c2 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -33,7 +33,7 @@
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
-from framework.context import get_ctx
+from api.context import get_ctx
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 0b0b7bc931..f4833340a6 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -23,7 +23,7 @@
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.context import filter_cores
+from api.context import filter_cores
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 6/8] dts: move params directory from framework to API
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
` (4 preceding siblings ...)
2026-04-23 17:03 ` [PATCH v1 5/8] dts: move context " Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 17:03 ` [PATCH v1 7/8] dts: separate Linux session into interface and logic Dean Marx
` (2 subsequent siblings)
8 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The params directory is imported in test suites such as
packet capture to use as a base class for dumpcap.
Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/api/cryptodev/__init__.py | 4 ++--
dts/api/cryptodev/config.py | 4 ++--
dts/api/packet.py | 4 ++--
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 2 +-
dts/{framework => api}/params/types.py | 2 +-
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 4 ++--
dts/api/testpmd/config.py | 6 +++---
dts/framework/remote_session/blocking_app.py | 6 +++---
dts/framework/remote_session/dpdk.py | 4 ++--
dts/framework/remote_session/dpdk_shell.py | 4 ++--
dts/framework/remote_session/interactive_shell.py | 4 ++--
dts/framework/test_run.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
18 files changed, 28 insertions(+), 28 deletions(-)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (97%)
rename dts/{framework => api}/params/types.py (99%)
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 67dcb02130..bbfe3622c2 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -14,6 +14,7 @@
from typing_extensions import Unpack
+from api.context import get_ctx
from api.cryptodev.config import CryptoPmdParams, TestType
from api.cryptodev.types import (
CryptodevResults,
@@ -23,11 +24,10 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
- from framework.params.types import CryptoPmdParamsDict
+ from api.params.types import CryptoPmdParamsDict
from pathlib import PurePath
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index a88e70d45c..3420c2fe91 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.params import Params, Switch
+from api.params.eal import EalParams
from api.utils import StrEnum
-from framework.params import Params, Switch
-from framework.params.eal import EalParams
Silent = Literal[""]
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 393fee542b..873b8f0324 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,6 +27,7 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.context import get_ctx
from api.exception import InternalError
from api.test import fail, log_debug
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
@@ -36,7 +37,6 @@
PerformanceTrafficStats,
)
from api.utils import get_packet_summaries
-from api.context import get_ctx
def send_packet_and_capture(
@@ -82,10 +82,10 @@ def send_packets_and_capture(
Returns:
A list of received packets.
"""
+ from api.context import get_ctx
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
- from api.context import get_ctx
assert isinstance(
get_ctx().func_tg, CapturingTrafficGenerator
diff --git a/dts/framework/params/__init__.py b/dts/api/params/__init__.py
similarity index 100%
rename from dts/framework/params/__init__.py
rename to dts/api/params/__init__.py
diff --git a/dts/framework/params/eal.py b/dts/api/params/eal.py
similarity index 97%
rename from dts/framework/params/eal.py
rename to dts/api/params/eal.py
index 86bfd3fcc6..64fa45ae12 100644
--- a/dts/framework/params/eal.py
+++ b/dts/api/params/eal.py
@@ -6,9 +6,9 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.params import Params, Switch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.virtual_device import VirtualDevice
-from framework.params import Params, Switch
if TYPE_CHECKING:
from api.testbed_model.port import Port
diff --git a/dts/framework/params/types.py b/dts/api/params/types.py
similarity index 99%
rename from dts/framework/params/types.py
rename to dts/api/params/types.py
index f2fa69f8b8..55a0e546c1 100644
--- a/dts/framework/params/types.py
+++ b/dts/api/params/types.py
@@ -32,6 +32,7 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
+from api.params import Switch, YesNoSwitch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.port import Port
from api.testbed_model.virtual_device import VirtualDevice
@@ -56,7 +57,6 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TXRingParams,
TxUDPPortPair,
)
-from framework.params import Switch, YesNoSwitch
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/api/test.py b/dts/api/test.py
index a1f2326075..03846639ad 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
-from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.context import get_ctx
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index a528663c21..e9cd822bac 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,7 +32,9 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.context import get_ctx
from api.exception import InteractiveCommandExecutionError, InternalError
+from api.params.types import TestPmdParamsDict
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
@@ -55,8 +57,6 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from api.context import get_ctx
-from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
from framework.settings import SETTINGS
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index 8b688834ee..96fe5e79fb 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,8 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
-from api.utils import StrEnum
-from framework.params import (
+from api.params import (
Params,
Switch,
YesNoSwitch,
@@ -24,7 +23,8 @@
modify_str,
str_from_flag_value,
)
-from framework.params.eal import EalParams
+from api.params.eal import EalParams
+from api.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 955a7ccdba..537d937eca 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -30,10 +30,10 @@
from typing_extensions import Self
-from api.testbed_model.node import Node
from api.context import get_ctx
-from framework.params import Params
-from framework.params.eal import EalParams
+from api.params import Params
+from api.params.eal import EalParams
+from api.testbed_model.node import Node
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 68897bfefe..afdf7526d9 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,7 +13,9 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
@@ -30,9 +32,7 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
-from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 947f60f75c..61cc4687f3 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -10,9 +10,9 @@
from abc import ABC, abstractmethod
from pathlib import PurePath
-from api.testbed_model.cpu import LogicalCoreList
from api.context import get_ctx
-from framework.params.eal import EalParams
+from api.params.eal import EalParams
+from api.testbed_model.cpu import LogicalCoreList
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index 6aebef3f45..f7f0669eea 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,15 +29,15 @@
from paramiko import Channel, channel
from typing_extensions import Self
+from api.context import get_ctx
from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from api.params import Params
from api.testbed_model.node import Node
-from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
-from framework.params import Params
from framework.settings import SETTINGS
P = ParamSpec("P")
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 914bf9491d..c133fbecb0 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,6 +106,7 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from api.testbed_model.capability import (
@@ -117,7 +118,6 @@
from api.testbed_model.topology import PortLink, Topology
from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
-from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 67ebbc64c2..d2a6cbab94 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -13,6 +13,7 @@
LinkTopology,
requires_link_topology,
)
+from api.context import get_ctx
from api.cryptodev import Cryptodev
from api.cryptodev.config import (
AeadAlgName,
@@ -33,7 +34,6 @@
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
-from api.context import get_ctx
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index f4833340a6..f237821a04 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -13,6 +13,7 @@
requires_link_topology,
requires_nic_capability,
)
+from api.context import filter_cores
from api.packet import (
get_expected_packets,
match_all_packets,
@@ -23,7 +24,6 @@
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from api.context import filter_cores
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index fd5cef5268..ba67c9e1c6 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -35,6 +35,7 @@
match_all_packets,
send_packets_and_capture,
)
+from api.params import Params
from api.test import verify
from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
@@ -42,7 +43,6 @@
PacketFilteringConfig,
)
from api.testpmd import TestPmd
-from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index f1eb435759..24f2cebf17 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -21,11 +21,11 @@
requires_link_topology,
)
from api.packet import assess_performance_by_packet
+from api.params.types import TestPmdParamsDict
from api.test import verify, write_performance_json
from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
-from framework.params.types import TestPmdParamsDict
class Config(BaseConfig):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 7/8] dts: separate Linux session into interface and logic
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
` (5 preceding siblings ...)
2026-04-23 17:03 ` [PATCH v1 6/8] dts: move params directory " Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 17:03 ` [PATCH v1 8/8] dts: update API rst files for doc build Dean Marx
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
8 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 34498 bytes --]
Separate Linux session into an interface for the API,
and a logical module in the framework.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
dts/api/testbed_model/linux_session.py | 366 +-----------------------
dts/api/testbed_model/node.py | 11 +-
dts/api/testbed_model/os_session.py | 16 ++
dts/framework/linux_session.py | 375 +++++++++++++++++++++++++
4 files changed, 414 insertions(+), 354 deletions(-)
create mode 100644 dts/framework/linux_session.py
diff --git a/dts/api/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
index 7307b2abe2..da3da4461c 100644
--- a/dts/api/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -2,366 +2,26 @@
# Copyright(c) 2023 PANTHEON.tech s.r.o.
# Copyright(c) 2023 University of New Hampshire
-"""Linux OS translator.
+"""Linux OS session interface.
-Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
-compliant with POSIX standards, so this module only implements the parts that aren't.
-This intermediate module implements the common parts of mostly POSIX compliant distributions.
+Extends the base :class:`~.os_session.OSSession` with methods specific to Linux nodes.
+The concrete implementation containing all backend logic lives in the framework package.
"""
-import json
-import re
-from collections.abc import Iterable
-from functools import cached_property
-from pathlib import PurePath
-from typing import TypedDict
+from abc import ABC, abstractmethod
-from typing_extensions import NotRequired
-from api.exception import (
- ConfigurationError,
- InternalError,
- RemoteCommandExecutionError,
-)
-from api.testbed_model.port import PortInfo
-from api.utils import expand_range
+class LinuxSession(ABC):
+ """Abstract interface for Linux-specific OS session operations.
-from .cpu import LogicalCore
-from .port import Port
-from .posix_session import PosixSession
-
-
-class LshwConfigurationOutput(TypedDict):
- """The relevant parts of ``lshw``'s ``configuration`` section."""
-
- #:
- driver: str
- #:
- link: str
-
-
-class LshwOutput(TypedDict):
- """A model of the relevant information from ``lshw``'s json output.
-
- Example:
- ::
-
- {
- ...
- "businfo" : "pci@0000:08:00.0",
- "logicalname" : "enp8s0",
- "version" : "00",
- "serial" : "52:54:00:59:e1:ac",
- ...
- "configuration" : {
- ...
- "link" : "yes",
- ...
- },
- ...
+ API consumers should type-hint against this class when they need access
+ to Linux-only capabilities beyond the base :class:`~.os_session.OSSession` contract.
"""
- #:
- businfo: str
- #:
- logicalname: NotRequired[str]
- #:
- serial: NotRequired[str]
- #:
- configuration: LshwConfigurationOutput
-
-
-class LinuxSession(PosixSession):
- """The implementation of non-Posix compliant parts of Linux."""
-
- @staticmethod
- def _get_privileged_command(command: str) -> str:
- command = command.replace(r"'", r"\'")
- return f"sudo -- sh -c '{command}'"
-
- def get_remote_cpus(self) -> list[LogicalCore]:
- """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
- cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
- lcores = []
- for cpu_line in cpu_info.splitlines():
- lcore, core, socket, node = map(int, cpu_line.split(","))
- lcores.append(LogicalCore(lcore, core, socket, node))
- return lcores
-
- def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
- """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
- return dpdk_prefix
-
- def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
-
- Raises:
- ConfigurationError: If the given `hugepage_size` is not supported by the OS.
- """
- self._logger.info("Getting Hugepage information.")
- if (
- f"hugepages-{hugepage_size}kB"
- not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
- ):
- raise ConfigurationError("hugepage size not supported by operating system")
- hugepages_total = self._get_hugepages_total(hugepage_size)
- self._numa_nodes = self._get_numa_nodes()
-
- if force_first_numa or hugepages_total < number_of:
- # when forcing numa, we need to clear existing hugepages regardless
- # of size, so they can be moved to the first numa node
- self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
- else:
- self._logger.info("Hugepages already configured.")
- self._mount_huge_pages()
-
- def _get_hugepages_total(self, hugepage_size: int) -> int:
- hugepages_total = self.send_command(
- f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
- ).stdout
- return int(hugepages_total)
-
- def _get_numa_nodes(self) -> list[int]:
- try:
- numa_count = self.send_command(
- "cat /sys/devices/system/node/online", verify=True
- ).stdout
- numa_range = expand_range(numa_count)
- except RemoteCommandExecutionError:
- # the file doesn't exist, meaning the node doesn't support numa
- numa_range = []
- return numa_range
-
- def _mount_huge_pages(self) -> None:
- self._logger.info("Re-mounting Hugepages.")
- hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
- self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
- result = self.send_command(hugapge_fs_cmd)
- if result.stdout == "":
- remote_mount_path = "/mnt/huge"
- self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
- self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
-
- def _supports_numa(self) -> bool:
- # the system supports numa if self._numa_nodes is non-empty and there are more
- # than one numa node (in the latter case it may actually support numa, but
- # there's no reason to do any numa specific configuration)
- return len(self._numa_nodes) > 1
-
- def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
- self._logger.info("Configuring Hugepages.")
- hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
- if force_first_numa and self._supports_numa():
- # clear non-numa hugepages
- self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
- hugepage_config_path = (
- f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
- f"/hugepages-{size}kB/nr_hugepages"
- )
-
- self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
-
- def get_port_info(self, pci_address: str) -> PortInfo:
- """Overrides :meth:`~.os_session.OSSession.get_port_info`.
-
- Raises:
- ConfigurationError: If the port could not be found.
- """
- bus_info = f"pci@{pci_address}"
- port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
- if port is None:
- raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
-
- logical_name = port.get("logicalname", "")
- mac_address = port.get("serial", "")
-
- configuration = port.get("configuration", {})
- driver = configuration.get("driver", "")
- is_link_up = configuration.get("link", "down") == "up"
-
- return PortInfo(mac_address, logical_name, driver, is_link_up)
-
- def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
-
- The :attr:`~.devbind_script_path` property must be setup in order to call this method.
- """
- ports_pci_addrs = " ".join(port.pci for port in ports)
-
- self.send_command(
- f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
- privileged=True,
- verify=True,
- )
-
- del self._lshw_net_info
-
- def bring_up_link(self, ports: Iterable[Port]) -> None:
- """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
- for port in ports:
- self.send_command(
- f"ip link set dev {port.logical_name} up", privileged=True, verify=True
- )
-
- del self._lshw_net_info
-
- def set_interface_link_up(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
- self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
-
- def delete_interface(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
- self.send_command(f"ip link delete {name}", privileged=True)
-
- @cached_property
- def devbind_script_path(self) -> PurePath:
- """The path to the dpdk-devbind.py script on the node.
-
- Needs to be manually assigned first in order to be used.
-
- Raises:
- InternalError: If accessed before environment setup.
- """
- raise InternalError("Accessed devbind script path before setup.")
-
- def load_vfio(self, pf_port: Port) -> None:
- """Overrides :meth:`~os_session.OSSession,load_vfio`."""
- cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
- device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
- if device and device.group(1) in ["37c8", "0435", "19e2"]:
- self.send_command(
- "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
- privileged=True,
- )
- self.send_command(
- "modprobe -r vfio_virqfd; modprobe -r vfio",
- privileged=True,
- )
- self.send_command(
- "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
- )
- self.send_command(
- "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
- privileged=True,
- )
- else:
- self.send_command("modprobe vfio-pci")
- self.refresh_lshw()
-
- def create_crypto_vfs(self, pf_port: list[Port]) -> None:
- """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
-
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
- """
- for port in pf_port:
- self.delete_crypto_vfs(port)
- for port in pf_port:
- sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
- )
- self.send_command(
- f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
- )
-
- self.refresh_lshw()
-
- def create_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+ @abstractmethod
+ def configure_ipv4_forwarding(self, enable: bool) -> None:
+ """Enable or disable IPv4 forwarding on the node.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ enable: True to enable forwarding, False to disable.
"""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- if curr_num_vfs == 0:
- self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
- self.refresh_lshw()
-
- def delete_crypto_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
- self.send_command(
- f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
- privileged=True,
- )
- self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
-
- def delete_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if curr_num_vfs == 0:
- self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
- else:
- self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
-
- def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- f"readlink {sys_bus_path}/virtfn*",
- privileged=True,
- )
- return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
- return []
-
- def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
- + f"{sys_bus_path}/virtfn*/uevent",
- privileged=True,
- )
- return pci_addrs.stdout.splitlines()
- else:
- return []
-
- @cached_property
- def _lshw_net_info(self) -> list[LshwOutput]:
- output = self.send_command("lshw -quiet -json -C network", verify=True)
- return json.loads(output.stdout)
-
- def refresh_lshw(self) -> None:
- """Force refresh of cached lshw network info."""
- if "_lshw_net_info" in self.__dict__:
- del self.__dict__["_lshw_net_info"]
- _ = self._lshw_net_info
-
- def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
- if attr_value:
- setattr(port, attr_name, attr_value)
- self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
- else:
- self._logger.warning(
- f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
- )
-
- def configure_port_mtu(self, mtu: int, port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
- self.send_command(
- f"ip link set dev {port.logical_name} mtu {mtu}",
- privileged=True,
- verify=True,
- )
-
- def configure_ipv4_forwarding(self, enable: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
- state = 1 if enable else 0
- self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
diff --git a/dts/api/testbed_model/node.py b/dts/api/testbed_model/node.py
index 40dd7f0666..3cab51855d 100644
--- a/dts/api/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -25,7 +25,6 @@
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
-from .linux_session import LinuxSession
from .os_session import OSSession, OSSessionInfo
from .port import Port
@@ -201,16 +200,26 @@ def close(self) -> None:
def create_session(node_config: NodeConfiguration, name: str, logger: DTSLogger) -> OSSession:
"""Factory for OS-aware sessions.
+ Creates a concrete :class:`~.os_session.OSSession` implementation appropriate for the
+ operating system specified in `node_config`. The concrete session classes live in the
+ framework package and are imported lazily to avoid circular dependencies between the
+ API and framework layers.
+
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.
+ Returns:
+ An OS-aware session connected to the node.
+
Raises:
ConfigurationError: If the node's OS is unsupported.
"""
match node_config.os:
case OS.linux:
+ from framework.linux_session import LinuxSession
+
return LinuxSession(node_config, name, logger)
case _:
raise ConfigurationError(f"Unsupported OS {node_config.os}")
diff --git a/dts/api/testbed_model/os_session.py b/dts/api/testbed_model/os_session.py
index b1e0538ac9..618a5bc45c 100644
--- a/dts/api/testbed_model/os_session.py
+++ b/dts/api/testbed_model/os_session.py
@@ -166,6 +166,22 @@ def close(self) -> None:
"""Close the underlying remote session."""
self.remote_session.close()
+ @property
+ @abstractmethod
+ def devbind_script_path(self) -> PurePath:
+ """The path to the dpdk-devbind.py script on the node.
+
+ Must be set up during environment initialization before access.
+
+ Raises:
+ InternalError: If accessed before environment setup.
+ """
+
+ @devbind_script_path.setter
+ @abstractmethod
+ def devbind_script_path(self, value: PurePath) -> None:
+ """Set the devbind script path after environment setup."""
+
@staticmethod
@abstractmethod
def _get_privileged_command(command: str) -> str:
diff --git a/dts/framework/linux_session.py b/dts/framework/linux_session.py
new file mode 100644
index 0000000000..54c6370d53
--- /dev/null
+++ b/dts/framework/linux_session.py
@@ -0,0 +1,375 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2023 University of New Hampshire
+
+"""Linux OS session implementation.
+
+Translates OS-unaware calls into Linux-specific commands and utilities.
+Implements the :class:`~api.linux_session.LinuxSession` contract for Linux distributions,
+building on :class:`~.posix_session.PosixSession` for POSIX-compliant operations.
+"""
+
+import json
+import re
+from collections.abc import Iterable
+from pathlib import PurePath
+from typing import TypedDict
+
+from typing_extensions import NotRequired
+
+from api.exception import (
+ ConfigurationError,
+ InternalError,
+ RemoteCommandExecutionError,
+)
+from api.testbed_model.cpu import LogicalCore
+from api.testbed_model.linux_session import LinuxSession as LinuxSessionBase
+from api.testbed_model.port import Port, PortInfo
+from api.testbed_model.posix_session import PosixSession
+from api.utils import expand_range
+
+
+class LshwConfigurationOutput(TypedDict):
+ """The relevant parts of ``lshw``'s ``configuration`` section."""
+
+ #:
+ driver: str
+ #:
+ link: str
+
+
+class LshwOutput(TypedDict):
+ """A model of the relevant information from ``lshw``'s json output.
+
+ Example:
+ ::
+
+ {
+ ...
+ "businfo" : "pci@0000:08:00.0",
+ "logicalname" : "enp8s0",
+ "version" : "00",
+ "serial" : "52:54:00:59:e1:ac",
+ ...
+ "configuration" : {
+ ...
+ "link" : "yes",
+ ...
+ },
+ ...
+ """
+
+ #:
+ businfo: str
+ #:
+ logicalname: NotRequired[str]
+ #:
+ serial: NotRequired[str]
+ #:
+ configuration: LshwConfigurationOutput
+
+
+class LinuxSession(PosixSession, LinuxSessionBase):
+ """Linux-specific implementation of the OS session interface.
+
+ Inherits POSIX-compliant operations from
+ :class:`~.posix_session.PosixSession` and implements the
+ :class:`~api.linux_session.LinuxSession` contract. All backend logic —
+ ``lshw`` caching, hugepage sysfs interaction, VFIO module loading,
+ SR-IOV management — lives here in the framework.
+ """
+
+ _devbind_script_path: PurePath | None
+
+ def __init__(self, *args, **kwargs) -> None:
+ """Overrides :meth:`~.os_session.OSSession.__init__`."""
+ self._devbind_script_path = None
+ super().__init__(*args, **kwargs)
+
+ @staticmethod
+ def _get_privileged_command(command: str) -> str:
+ command = command.replace(r"'", r"\'")
+ return f"sudo -- sh -c '{command}'"
+
+ def get_remote_cpus(self) -> list[LogicalCore]:
+ """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
+ cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
+ lcores = []
+ for cpu_line in cpu_info.splitlines():
+ lcore, core, socket, node = map(int, cpu_line.split(","))
+ lcores.append(LogicalCore(lcore, core, socket, node))
+ return lcores
+
+ def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
+ """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
+ return dpdk_prefix
+
+ @property
+ def devbind_script_path(self) -> PurePath:
+ """Overrides :attr:`~.os_session.OSSession.devbind_script_path`.
+
+ Raises:
+ InternalError: If accessed before environment setup.
+ """
+ if self._devbind_script_path is None:
+ raise InternalError("Accessed devbind script path before setup.")
+ return self._devbind_script_path
+
+ @devbind_script_path.setter
+ def devbind_script_path(self, value: PurePath) -> None:
+ """Set the devbind script path after environment setup."""
+ self._devbind_script_path = value
+
+ def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
+
+ Raises:
+ ConfigurationError: If the given `hugepage_size` is not supported by the OS.
+ """
+ self._logger.info("Getting Hugepage information.")
+ if (
+ f"hugepages-{hugepage_size}kB"
+ not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
+ ):
+ raise ConfigurationError("hugepage size not supported by operating system")
+ hugepages_total = self._get_hugepages_total(hugepage_size)
+ self._numa_nodes = self._get_numa_nodes()
+
+ if force_first_numa or hugepages_total < number_of:
+ self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
+ else:
+ self._logger.info("Hugepages already configured.")
+ self._mount_huge_pages()
+
+ def _get_hugepages_total(self, hugepage_size: int) -> int:
+ hugepages_total = self.send_command(
+ f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
+ ).stdout
+ return int(hugepages_total)
+
+ def _get_numa_nodes(self) -> list[int]:
+ try:
+ numa_count = self.send_command(
+ "cat /sys/devices/system/node/online", verify=True
+ ).stdout
+ numa_range = expand_range(numa_count)
+ except RemoteCommandExecutionError:
+ numa_range = []
+ return numa_range
+
+ def _mount_huge_pages(self) -> None:
+ self._logger.info("Re-mounting Hugepages.")
+ hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
+ self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
+ result = self.send_command(hugapge_fs_cmd)
+ if result.stdout == "":
+ remote_mount_path = "/mnt/huge"
+ self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
+ self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
+
+ def _supports_numa(self) -> bool:
+ return len(self._numa_nodes) > 1
+
+ def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
+ self._logger.info("Configuring Hugepages.")
+ hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
+ if force_first_numa and self._supports_numa():
+ self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
+ hugepage_config_path = (
+ f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
+ f"/hugepages-{size}kB/nr_hugepages"
+ )
+ self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
+
+ def get_port_info(self, pci_address: str) -> PortInfo:
+ """Overrides :meth:`~.os_session.OSSession.get_port_info`.
+
+ Raises:
+ ConfigurationError: If the port could not be found.
+ """
+ bus_info = f"pci@{pci_address}"
+ port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
+ if port is None:
+ raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
+
+ logical_name = port.get("logicalname", "")
+ mac_address = port.get("serial", "")
+
+ configuration = port.get("configuration", {})
+ driver = configuration.get("driver", "")
+ is_link_up = configuration.get("link", "down") == "up"
+
+ return PortInfo(mac_address, logical_name, driver, is_link_up)
+
+ @property
+ def _lshw_net_info(self) -> list[LshwOutput]:
+ """Cached lshw network info, fetched on first access."""
+ if not hasattr(self, "_lshw_net_cache"):
+ output = self.send_command("lshw -quiet -json -C network", verify=True)
+ self._lshw_net_cache: list[LshwOutput] = json.loads(output.stdout)
+ return self._lshw_net_cache
+
+ def _refresh_lshw(self) -> None:
+ """Force refresh of cached lshw network info.
+
+ Called internally after operations that change port/driver state.
+ """
+ if hasattr(self, "_lshw_net_cache"):
+ del self._lshw_net_cache
+ _ = self._lshw_net_info
+
+ def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
+ if attr_value:
+ setattr(port, attr_name, attr_value)
+ self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
+ else:
+ self._logger.warning(
+ f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
+ )
+
+ def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`."""
+ ports_pci_addrs = " ".join(port.pci for port in ports)
+ self.send_command(
+ f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
+ privileged=True,
+ verify=True,
+ )
+ self._refresh_lshw()
+
+ def bring_up_link(self, ports: Iterable[Port]) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
+ for port in ports:
+ self.send_command(
+ f"ip link set dev {port.logical_name} up", privileged=True, verify=True
+ )
+ self._refresh_lshw()
+
+ def set_interface_link_up(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
+ self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
+
+ def delete_interface(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
+ self.send_command(f"ip link delete {name}", privileged=True)
+
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self._refresh_lshw()
+
+ def create_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ if curr_num_vfs == 0:
+ self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ self._refresh_lshw()
+
+ def delete_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if curr_num_vfs == 0:
+ self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
+ else:
+ self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+
+ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
+ + f"{sys_bus_path}/virtfn*/uevent",
+ privileged=True,
+ )
+ return pci_addrs.stdout.splitlines()
+ else:
+ return []
+
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~.os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+ self._refresh_lshw()
+
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
+ def configure_port_mtu(self, mtu: int, port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
+ self.send_command(
+ f"ip link set dev {port.logical_name} mtu {mtu}",
+ privileged=True,
+ verify=True,
+ )
+
+ def configure_ipv4_forwarding(self, enable: bool) -> None:
+ """Implements :meth:`~api.linux_session.LinuxSession.configure_ipv4_forwarding`."""
+ state = 1 if enable else 0
+ self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v1 8/8] dts: update API rst files for doc build
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
` (6 preceding siblings ...)
2026-04-23 17:03 ` [PATCH v1 7/8] dts: separate Linux session into interface and logic Dean Marx
@ 2026-04-23 17:03 ` Dean Marx
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
8 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 17:03 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Appropriately rename/update the framework rst files
which have been moved to the API in this series.
Resolve circular dependency issues in api/params/types
and api/testpmd.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
...{framework.context.rst => api.context.rst} | 2 +-
...mework.exception.rst => api.exception.rst} | 2 +-
...work.params.eal.rst => api.params.eal.rst} | 2 +-
.../{framework.params.rst => api.params.rst} | 6 ++--
....params.types.rst => api.params.types.rst} | 2 +-
...work.test_suite.rst => api.test_suite.rst} | 2 +-
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 +++++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
.../{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/framework.linux_session.rst | 8 ++++++
doc/api/dts/framework.testbed_model.rst | 28 -------------------
...mework.testbed_model.traffic_generator.rst | 16 -----------
doc/api/dts/index.rst | 15 +++++-----
dts/api/params/types.py | 8 ++++--
dts/api/testpmd/__init__.py | 7 ++++-
27 files changed, 93 insertions(+), 75 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
create mode 100644 doc/api/dts/framework.linux_session.rst
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
diff --git a/doc/api/dts/framework.context.rst b/doc/api/dts/api.context.rst
similarity index 80%
rename from doc/api/dts/framework.context.rst
rename to doc/api/dts/api.context.rst
index 925c160360..65b4ab9a47 100644
--- a/doc/api/dts/framework.context.rst
+++ b/doc/api/dts/api.context.rst
@@ -3,6 +3,6 @@
context - DTS execution context
===============================
-.. automodule:: framework.context
+.. automodule:: api.context
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.exception.rst b/doc/api/dts/api.exception.rst
similarity index 77%
rename from doc/api/dts/framework.exception.rst
rename to doc/api/dts/api.exception.rst
index efb47dc5ae..8e6bff5ee7 100644
--- a/doc/api/dts/framework.exception.rst
+++ b/doc/api/dts/api.exception.rst
@@ -3,6 +3,6 @@
exception - Exceptions
======================
-.. automodule:: framework.exception
+.. automodule:: api.exception
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.params.eal.rst b/doc/api/dts/api.params.eal.rst
similarity index 79%
rename from doc/api/dts/framework.params.eal.rst
rename to doc/api/dts/api.params.eal.rst
index 6999b00233..4531cb1fe1 100644
--- a/doc/api/dts/framework.params.eal.rst
+++ b/doc/api/dts/api.params.eal.rst
@@ -3,6 +3,6 @@
eal - EAL Parameters Modelling
==============================
-.. automodule:: framework.params.eal
+.. automodule:: api.params.eal
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.params.rst b/doc/api/dts/api.params.rst
similarity index 71%
rename from doc/api/dts/framework.params.rst
rename to doc/api/dts/api.params.rst
index d8c6af9667..3ea7f9215e 100644
--- a/doc/api/dts/framework.params.rst
+++ b/doc/api/dts/api.params.rst
@@ -3,7 +3,7 @@
params - Command Line Parameters Modelling
==========================================
-.. automodule:: framework.params
+.. automodule:: api.params
:members:
:show-inheritance:
@@ -11,5 +11,5 @@ params - Command Line Parameters Modelling
:hidden:
:maxdepth: 1
- framework.params.eal
- framework.params.types
+ api.params.eal
+ api.params.types
diff --git a/doc/api/dts/framework.params.types.rst b/doc/api/dts/api.params.types.rst
similarity index 80%
rename from doc/api/dts/framework.params.types.rst
rename to doc/api/dts/api.params.types.rst
index 6d609038be..4754b3a665 100644
--- a/doc/api/dts/framework.params.types.rst
+++ b/doc/api/dts/api.params.types.rst
@@ -3,6 +3,6 @@
params.types - Parameters Modelling Types
=========================================
-.. automodule:: framework.params.types
+.. automodule:: api.params.types
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.test_suite.rst b/doc/api/dts/api.test_suite.rst
similarity index 81%
rename from doc/api/dts/framework.test_suite.rst
rename to doc/api/dts/api.test_suite.rst
index 9517f51a4a..4acb7b103a 100644
--- a/doc/api/dts/framework.test_suite.rst
+++ b/doc/api/dts/api.test_suite.rst
@@ -3,6 +3,6 @@
test\_suite - Common Test Suite Features
========================================
-.. automodule:: framework.test_suite
+.. automodule:: api.test_suite
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.capability.rst b/doc/api/dts/api.testbed_model.capability.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.capability.rst
rename to doc/api/dts/api.testbed_model.capability.rst
index fab91cad83..88e396dddb 100644
--- a/doc/api/dts/framework.testbed_model.capability.rst
+++ b/doc/api/dts/api.testbed_model.capability.rst
@@ -3,6 +3,6 @@
capability - Testbed Capabilities
=================================
-.. automodule:: framework.testbed_model.capability
+.. automodule:: api.testbed_model.capability
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.cpu.rst b/doc/api/dts/api.testbed_model.cpu.rst
similarity index 78%
rename from doc/api/dts/framework.testbed_model.cpu.rst
rename to doc/api/dts/api.testbed_model.cpu.rst
index 997f2a9795..dbbb29480a 100644
--- a/doc/api/dts/framework.testbed_model.cpu.rst
+++ b/doc/api/dts/api.testbed_model.cpu.rst
@@ -3,6 +3,6 @@
cpu - CPU Representation and Utilities
======================================
-.. automodule:: framework.testbed_model.cpu
+.. automodule:: api.testbed_model.cpu
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.linux_session.rst b/doc/api/dts/api.testbed_model.linux_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.linux_session.rst
rename to doc/api/dts/api.testbed_model.linux_session.rst
index 7567816199..cfe79d8bca 100644
--- a/doc/api/dts/framework.testbed_model.linux_session.rst
+++ b/doc/api/dts/api.testbed_model.linux_session.rst
@@ -3,6 +3,6 @@
linux\_session - Linux Remote Session
=====================================
-.. automodule:: framework.testbed_model.linux_session
+.. automodule:: api.testbed_model.linux_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.node.rst b/doc/api/dts/api.testbed_model.node.rst
similarity index 71%
rename from doc/api/dts/framework.testbed_model.node.rst
rename to doc/api/dts/api.testbed_model.node.rst
index 23c6c46a00..15f522e5f7 100644
--- a/doc/api/dts/framework.testbed_model.node.rst
+++ b/doc/api/dts/api.testbed_model.node.rst
@@ -3,6 +3,6 @@
node - Base Node
================
-.. automodule:: framework.testbed_model.node
+.. automodule:: api.testbed_model.node
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.os_session.rst b/doc/api/dts/api.testbed_model.os_session.rst
similarity index 76%
rename from doc/api/dts/framework.testbed_model.os_session.rst
rename to doc/api/dts/api.testbed_model.os_session.rst
index ecfb352311..e7e3f9894f 100644
--- a/doc/api/dts/framework.testbed_model.os_session.rst
+++ b/doc/api/dts/api.testbed_model.os_session.rst
@@ -3,6 +3,6 @@
os\_session - OS-aware Remote Session ABC
=========================================
-.. automodule:: framework.testbed_model.os_session
+.. automodule:: api.testbed_model.os_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.port.rst b/doc/api/dts/api.testbed_model.port.rst
similarity index 77%
rename from doc/api/dts/framework.testbed_model.port.rst
rename to doc/api/dts/api.testbed_model.port.rst
index fdb7ca8a1d..d64501aef0 100644
--- a/doc/api/dts/framework.testbed_model.port.rst
+++ b/doc/api/dts/api.testbed_model.port.rst
@@ -3,7 +3,7 @@
port - NIC Port Representation
==============================
-.. automodule:: framework.testbed_model.port
+.. automodule:: api.testbed_model.port
:members:
:show-inheritance:
:noindex:
diff --git a/doc/api/dts/framework.testbed_model.posix_session.rst b/doc/api/dts/api.testbed_model.posix_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.posix_session.rst
rename to doc/api/dts/api.testbed_model.posix_session.rst
index e65585fd85..9f0e9ff18d 100644
--- a/doc/api/dts/framework.testbed_model.posix_session.rst
+++ b/doc/api/dts/api.testbed_model.posix_session.rst
@@ -3,6 +3,6 @@
posix\_session - Posix Remote Session
=====================================
-.. automodule:: framework.testbed_model.posix_session
+.. automodule:: api.testbed_model.posix_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.rst b/doc/api/dts/api.testbed_model.rst
new file mode 100644
index 0000000000..5e2e5189b2
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.rst
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+testbed\_model - Testbed Modelling Package
+==========================================
+
+.. automodule:: api.testbed_model
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 2
+
+ api.testbed_model.traffic_generator
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.os_session
+ api.testbed_model.linux_session
+ api.testbed_model.posix_session
+ api.testbed_model.node
+ api.testbed_model.capability
+ api.testbed_model.cpu
+ api.testbed_model.port
+ api.testbed_model.topology
+ api.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.topology.rst b/doc/api/dts/api.testbed_model.topology.rst
similarity index 73%
rename from doc/api/dts/framework.testbed_model.topology.rst
rename to doc/api/dts/api.testbed_model.topology.rst
index 496f2a895f..bb63fe38dc 100644
--- a/doc/api/dts/framework.testbed_model.topology.rst
+++ b/doc/api/dts/api.testbed_model.topology.rst
@@ -3,6 +3,6 @@
topology - Testbed Topology
===========================
-.. automodule:: framework.testbed_model.topology
+.. automodule:: api.testbed_model.topology
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
similarity index 68%
rename from doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
index 29fa834042..cfe03201a7 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
@@ -3,6 +3,6 @@
capturing\_traffic\_generator - Base Capturing TG ABC
=====================================================
-.. automodule:: framework.testbed_model.traffic_generator.capturing_traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.capturing_traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.rst
new file mode 100644
index 0000000000..311bdcf6b9
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.traffic_generator.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+traffic\_generator Subpackage
+=============================
+
+.. automodule:: api.testbed_model.traffic_generator
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.traffic_generator.traffic_generator
+ api.testbed_model.traffic_generator.capturing_traffic_generator
+ api.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
similarity index 70%
rename from doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
index df78ac9514..949bb66632 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
@@ -3,6 +3,6 @@
scapy - Capturing Traffic Generator
===================================
-.. automodule:: framework.testbed_model.traffic_generator.scapy
+.. automodule:: api.testbed_model.traffic_generator.scapy
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
similarity index 65%
rename from doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
index bfec728dee..1045e534b5 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
@@ -3,6 +3,6 @@
traffic\_generator - Base TG ABC
================================
-.. automodule:: framework.testbed_model.traffic_generator.traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.virtual_device.rst b/doc/api/dts/api.testbed_model.virtual_device.rst
similarity index 72%
rename from doc/api/dts/framework.testbed_model.virtual_device.rst
rename to doc/api/dts/api.testbed_model.virtual_device.rst
index a6b0420e75..97adc895f6 100644
--- a/doc/api/dts/framework.testbed_model.virtual_device.rst
+++ b/doc/api/dts/api.testbed_model.virtual_device.rst
@@ -3,6 +3,6 @@
virtual\_device - Virtual Devices
=================================
-.. automodule:: framework.testbed_model.virtual_device
+.. automodule:: api.testbed_model.virtual_device
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.utils.rst b/doc/api/dts/api.utils.rst
similarity index 80%
rename from doc/api/dts/framework.utils.rst
rename to doc/api/dts/api.utils.rst
index cc06d4c3c3..0b4baff1b5 100644
--- a/doc/api/dts/framework.utils.rst
+++ b/doc/api/dts/api.utils.rst
@@ -3,6 +3,6 @@
utils - Various Utilities
=========================
-.. automodule:: framework.utils
+.. automodule:: api.utils
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.linux_session.rst b/doc/api/dts/framework.linux_session.rst
new file mode 100644
index 0000000000..5cb2d04db1
--- /dev/null
+++ b/doc/api/dts/framework.linux_session.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+linux\_session - Linux Remote Session
+=====================================
+
+.. automodule:: framework.linux_session
+ :members:
+ :show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.rst b/doc/api/dts/framework.testbed_model.rst
deleted file mode 100644
index f283178f6a..0000000000
--- a/doc/api/dts/framework.testbed_model.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-testbed\_model - Testbed Modelling Package
-==========================================
-
-.. automodule:: framework.testbed_model
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 2
-
- framework.testbed_model.traffic_generator
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.os_session
- framework.testbed_model.linux_session
- framework.testbed_model.posix_session
- framework.testbed_model.node
- framework.testbed_model.capability
- framework.testbed_model.cpu
- framework.testbed_model.port
- framework.testbed_model.topology
- framework.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.rst b/doc/api/dts/framework.testbed_model.traffic_generator.rst
deleted file mode 100644
index 24c250ee3a..0000000000
--- a/doc/api/dts/framework.testbed_model.traffic_generator.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-traffic\_generator Subpackage
-=============================
-
-.. automodule:: framework.testbed_model.traffic_generator
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.traffic_generator.traffic_generator
- framework.testbed_model.traffic_generator.capturing_traffic_generator
- framework.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index c719297c11..bdb8462622 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -16,9 +16,9 @@ Packages
tests
api
- framework.testbed_model
+ api.testbed_model
framework.remote_session
- framework.params
+ api.params
framework.config
Modules
@@ -29,14 +29,15 @@ Modules
framework.runner
framework.test_run
- framework.test_suite
+ framework.linux_session
+ api.test_suite
framework.test_result
framework.settings
- framework.context
+ api.context
framework.logger
framework.parser
- framework.utils
- framework.exception
+ api.utils
+ api.exception
Indices and tables
@@ -44,4 +45,4 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
-* :ref:`search`
+* :ref:`search`
\ No newline at end of file
diff --git a/dts/api/params/types.py b/dts/api/params/types.py
index 55a0e546c1..280c87ec92 100644
--- a/dts/api/params/types.py
+++ b/dts/api/params/types.py
@@ -12,8 +12,10 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
params = TestPmdParams(**kwargs)
"""
+from __future__ import annotations
+
from pathlib import PurePath
-from typing import TypedDict
+from typing import TYPE_CHECKING, TypedDict
from api.cryptodev.config import (
AeadAlgName,
@@ -32,7 +34,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
-from api.params import Switch, YesNoSwitch
+
+if TYPE_CHECKING:
+ from api.params import Switch, YesNoSwitch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.port import Port
from api.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9cd822bac..09974048e7 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -14,6 +14,8 @@
testpmd.close()
"""
+from __future__ import annotations
+
import functools
import re
import time
@@ -21,6 +23,7 @@
from enum import Flag
from pathlib import PurePath
from typing import (
+ TYPE_CHECKING,
Any,
Callable,
ClassVar,
@@ -34,7 +37,9 @@
from api.capabilities import LinkTopology, NicCapability
from api.context import get_ctx
from api.exception import InteractiveCommandExecutionError, InternalError
-from api.params.types import TestPmdParamsDict
+
+if TYPE_CHECKING:
+ from api.params.types import TestPmdParamsDict
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 0/7] dts: move test suite imports from framework to API
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
` (7 preceding siblings ...)
2026-04-23 17:03 ` [PATCH v1 8/8] dts: update API rst files for doc build Dean Marx
@ 2026-04-23 19:04 ` Dean Marx
2026-04-23 19:04 ` [PATCH v2 1/7] dts: move test suite module " Dean Marx
` (7 more replies)
8 siblings, 8 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 19:04 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
This series moves various modules from the framework directory
to the API based on which are being imported by test suites.
These include:
- test_suite
- testbed_model
- exception
- utils
- context
- params
This eliminates all test suite framework imports except for
the remote_session imports in packet_capture, as well
as the settings/config imports in smoke_tests. I believe these
imports, and what to do with them, should be a topic of discussion
in future DTS meetings, as I don't believe they should reside in the
API, even if they are being imported in test suites.
In addition to these changes, I've split the linux_session module
into an interface in api/testbed_model/linux_session and an
implementation in framework/linux_session. This way, users
can still import linux session if necessary, without exposing
the backend implementation.
---
v2:
* Integrated rst updates with corresponding commits for build testing
Dean Marx (7):
dts: move test suite module from framework to API
dts: move testbed model from framework to API
dts: move exception module from framework to API
dts: move utils from framework to API
dts: move context from framework to API
dts: move params directory from framework to API
dts: separate Linux session into interface and logic
--
2.52.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v2 1/7] dts: move test suite module from framework to API
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
@ 2026-04-23 19:04 ` Dean Marx
2026-04-23 19:04 ` [PATCH v2 2/7] dts: move testbed model " Dean Marx
` (6 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 19:04 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Currently, each test suite imports the TestSuite class
from the DTS framework to use as a base class.
However, the goal for 26.07 is to move all test suite
imports to the API module. Moves and updates the test_suite
file to the API directory, and updates all files that import
test_suite to reflect this change.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.test_suite.rst => api.test_suite.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/capabilities.py | 6 +++---
dts/api/packet.py | 2 +-
dts/{framework => api}/test_suite.py | 7 +++----
dts/framework/config/__init__.py | 4 ++--
dts/framework/config/test_run.py | 8 ++++----
dts/framework/context.py | 2 +-
dts/framework/test_run.py | 2 +-
dts/framework/testbed_model/capability.py | 6 +++---
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
| 2 +-
dts/tests/TestSuite_port_control.py | 2 +-
dts/tests/TestSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 2 +-
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 2 +-
dts/tests/TestSuite_vlan.py | 2 +-
37 files changed, 47 insertions(+), 48 deletions(-)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename dts/{framework => api}/test_suite.py (99%)
diff --git a/doc/api/dts/framework.test_suite.rst b/doc/api/dts/api.test_suite.rst
similarity index 81%
rename from doc/api/dts/framework.test_suite.rst
rename to doc/api/dts/api.test_suite.rst
index 9517f51a4a..4acb7b103a 100644
--- a/doc/api/dts/framework.test_suite.rst
+++ b/doc/api/dts/api.test_suite.rst
@@ -3,6 +3,6 @@
test\_suite - Common Test Suite Features
========================================
-.. automodule:: framework.test_suite
+.. automodule:: api.test_suite
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index c719297c11..b9338d6b11 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -29,7 +29,7 @@ Modules
framework.runner
framework.test_run
- framework.test_suite
+ api.test_suite
framework.test_result
framework.settings
framework.context
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 09bc538523..8569cacbd2 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -22,7 +22,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from framework.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -33,7 +33,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from framework.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -47,7 +47,7 @@ def test_scatter_mbuf_2048(self):
from typing import TYPE_CHECKING, Callable
if TYPE_CHECKING:
- from framework.test_suite import TestProtocol
+ from api.test_suite import TestProtocol
class LinkTopology(IntEnum):
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 094a1b7a9d..fc2be931fe 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -175,7 +175,7 @@ def adjust_addresses(packets: list[Packet], expected: bool = False) -> list[Pack
Raises:
InternalError: If no tests are running.
"""
- from framework.test_suite import TestSuite
+ from api.test_suite import TestSuite
if get_ctx().local.current_test_suite is None:
raise InternalError("No current test suite, tests aren't running?")
diff --git a/dts/framework/test_suite.py b/dts/api/test_suite.py
similarity index 99%
rename from dts/framework/test_suite.py
rename to dts/api/test_suite.py
index 69ce26040a..f107b1cd2c 100644
--- a/dts/framework/test_suite.py
+++ b/dts/api/test_suite.py
@@ -30,12 +30,11 @@
from typing_extensions import Self
from framework.config.common import FrozenModel
+from framework.exception import ConfigurationError, InternalError
+from framework.logger import DTSLogger, get_dts_logger
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
-
-from .exception import ConfigurationError, InternalError
-from .logger import DTSLogger, get_dts_logger
-from .utils import to_pascal_case
+from framework.utils import to_pascal_case
if TYPE_CHECKING:
from framework.context import Context
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index d2f0138e4a..6f4f9d82f8 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -43,7 +43,7 @@
# Import only if type checking or building docs, to prevent circular imports.
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig
+ from api.test_suite import BaseConfig
NodesConfig = Annotated[list[NodeConfiguration], Field(min_length=1)]
@@ -182,7 +182,7 @@ def load_config(ctx: ValidationContext) -> Configuration:
nodes = _load_and_parse_model(ctx["settings"].nodes_config_path, NodesConfig, ctx)
try:
- from framework.test_suite import BaseConfig as BaseConfig
+ from api.test_suite import BaseConfig as BaseConfig
Configuration.model_rebuild()
return Configuration.model_validate(
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 76e24d1785..75737418a8 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -33,7 +33,7 @@
from .common import FrozenModel, load_fields_from_settings
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
+ from api.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
@unique
@@ -230,7 +230,7 @@ class TestSuiteConfig(FrozenModel):
@cached_property
def test_suite_spec(self) -> "TestSuiteSpec":
"""The specification of the requested test suite."""
- from framework.test_suite import find_by_name
+ from api.test_suite import find_by_name
test_suite_spec = find_by_name(self.test_suite_name)
assert (
@@ -280,7 +280,7 @@ def fetch_all_test_suites() -> list[TestSuiteConfig]:
This function does not include the smoke tests.
"""
- from framework.test_suite import AVAILABLE_TEST_SUITES
+ from api.test_suite import AVAILABLE_TEST_SUITES
return [
TestSuiteConfig(test_suite=test_suite.name)
@@ -506,7 +506,7 @@ def filter_tests(
self, tests_config: dict[str, "BaseConfig"]
) -> Iterable[tuple[type["TestSuite"], "BaseConfig", deque[type["TestCase"]]]]:
"""Filter test suites and cases selected for execution."""
- from framework.test_suite import TestCaseType
+ from api.test_suite import TestCaseType
test_suites = [TestSuiteConfig(test_suite="smoke_tests")]
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 8f1021dc96..86745ab56b 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -16,8 +16,8 @@
from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
+ from api.test_suite import TestCase, TestSuite
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
- from framework.test_suite import TestCase, TestSuite
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..037ae6c9fb 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,6 +106,7 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
@@ -113,7 +114,6 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from framework.testbed_model.capability import (
Capability,
get_supported_capabilities,
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 960370fc72..39f6f3c0df 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -25,7 +25,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from framework.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -36,7 +36,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from framework.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -71,8 +71,8 @@ def test_scatter_mbuf_2048(self):
from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
+ from api.test_suite import TestCase
from api.testpmd import TestPmd
- from framework.test_suite import TestCase
P = ParamSpec("P")
TestPmdMethod = Callable[Concatenate["TestPmd", P], Any]
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index c57231de22..05bfe951fd 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,8 +11,8 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.port import Port
diff --git a/dts/tests/TestSuite_checksum_offload.py b/dts/tests/TestSuite_checksum_offload.py
index 90ca798e56..a2ea13991b 100644
--- a/dts/tests/TestSuite_checksum_offload.py
+++ b/dts/tests/TestSuite_checksum_offload.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import ChecksumOffloadOptions, PacketOffloadFlag
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_IPV4_CKSUM)
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index af0a5680ab..3f046ad87e 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -30,9 +30,9 @@
CryptodevResults,
)
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, crypto_test
from framework.context import get_ctx
from framework.exception import SkippedTestException
-from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
diff --git a/dts/tests/TestSuite_dual_vlan.py b/dts/tests/TestSuite_dual_vlan.py
index 1b77dd2b47..f3347a6d52 100644
--- a/dts/tests/TestSuite_dual_vlan.py
+++ b/dts/tests/TestSuite_dual_vlan.py
@@ -21,9 +21,9 @@
from api.capabilities import LinkTopology, requires_link_topology
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestDualVlan(TestSuite):
diff --git a/dts/tests/TestSuite_dynamic_config.py b/dts/tests/TestSuite_dynamic_config.py
index 7204ec4f73..b9e2c30da1 100644
--- a/dts/tests/TestSuite_dynamic_config.py
+++ b/dts/tests/TestSuite_dynamic_config.py
@@ -27,9 +27,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index 5ac85bee7d..e0ef1f447a 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -37,10 +37,10 @@
)
from api.packet import send_packets
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
from framework.exception import InteractiveCommandExecutionError
-from framework.test_suite import TestSuite, func_test
def setup_and_teardown_test(
diff --git a/dts/tests/TestSuite_hello_world.py b/dts/tests/TestSuite_hello_world.py
index bf1a93c782..cd62eb8f3e 100644
--- a/dts/tests/TestSuite_hello_world.py
+++ b/dts/tests/TestSuite_hello_world.py
@@ -9,8 +9,8 @@
"""
from api.test import log
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 596b892730..44fee58775 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -18,10 +18,10 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from framework.context import filter_cores
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
from framework.utils import generate_random_packets
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index a7e24b37d5..98c12459f6 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -25,9 +25,9 @@
)
from api.packet import send_packet_and_capture
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from framework.exception import InteractiveCommandExecutionError
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mtu.py b/dts/tests/TestSuite_mtu.py
index 8355495d33..c264db299e 100644
--- a/dts/tests/TestSuite_mtu.py
+++ b/dts/tests/TestSuite_mtu.py
@@ -23,8 +23,8 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
STANDARD_FRAME = 1518 # --max-pkt-len will subtract l2 information at a minimum of 18 bytes.
JUMBO_FRAME = 9018
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 4bd15e2401..d37796954d 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,11 +36,11 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreList
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py
index 96da67ee7d..6c7f6d79fe 100644
--- a/dts/tests/TestSuite_pmd_buffer_scatter.py
+++ b/dts/tests/TestSuite_pmd_buffer_scatter.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index f6adf262c3..8777d446cd 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -22,6 +22,7 @@
)
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import (
@@ -30,7 +31,6 @@
TestPmdVerbosePacket,
)
from framework.exception import InteractiveCommandExecutionError
-from framework.test_suite import BaseConfig, TestSuite, func_test
from framework.utils import StrEnum
diff --git a/dts/tests/TestSuite_port_control.py b/dts/tests/TestSuite_port_control.py
index 6be47838d0..5b960cb3a3 100644
--- a/dts/tests/TestSuite_port_control.py
+++ b/dts/tests/TestSuite_port_control.py
@@ -18,9 +18,9 @@
)
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_port_restart_config_persistency.py b/dts/tests/TestSuite_port_restart_config_persistency.py
index 4ea22b6d70..88df35d33c 100644
--- a/dts/tests/TestSuite_port_restart_config_persistency.py
+++ b/dts/tests/TestSuite_port_restart_config_persistency.py
@@ -14,9 +14,9 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import TestPmdPortFlowCtrl
-from framework.test_suite import TestSuite, func_test
ALTERNATIVE_MTU: int = 800
STANDARD_MTU: int = 1500
diff --git a/dts/tests/TestSuite_port_stats.py b/dts/tests/TestSuite_port_stats.py
index 3dc045f847..0328c6718c 100644
--- a/dts/tests/TestSuite_port_stats.py
+++ b/dts/tests/TestSuite_port_stats.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_promisc_support.py b/dts/tests/TestSuite_promisc_support.py
index a0c65dc662..c59c8c6078 100644
--- a/dts/tests/TestSuite_promisc_support.py
+++ b/dts/tests/TestSuite_promisc_support.py
@@ -21,8 +21,8 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_qinq.py b/dts/tests/TestSuite_qinq.py
index 505d71dbc8..5dde37d4db 100644
--- a/dts/tests/TestSuite_qinq.py
+++ b/dts/tests/TestSuite_qinq.py
@@ -18,8 +18,8 @@
from api.capabilities import NicCapability, requires_nic_capability
from api.packet import send_packet_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestQinq(TestSuite):
diff --git a/dts/tests/TestSuite_queue_start_stop.py b/dts/tests/TestSuite_queue_start_stop.py
index e9048d4245..6935f395c1 100644
--- a/dts/tests/TestSuite_queue_start_stop.py
+++ b/dts/tests/TestSuite_queue_start_stop.py
@@ -24,9 +24,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.TWO_LINKS)
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 6255e4c36d..3d74decb11 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -23,6 +23,7 @@
from api.capabilities import NicCapability, requires_nic_capability
from api.packet import send_packet_and_capture
from api.test import fail, log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import FlowRule
from framework.exception import (
@@ -30,7 +31,6 @@
SkippedTestException,
TestCaseVerifyError,
)
-from framework.test_suite import TestSuite, func_test
@dataclass
diff --git a/dts/tests/TestSuite_rx_tx_offload.py b/dts/tests/TestSuite_rx_tx_offload.py
index b0da627d3c..c8d24baaae 100644
--- a/dts/tests/TestSuite_rx_tx_offload.py
+++ b/dts/tests/TestSuite_rx_tx_offload.py
@@ -13,12 +13,12 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import (
OffloadConfiguration,
RxTxLiteralSwitch,
)
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.ONE_LINK)
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index 1e7ab7b036..f1eb435759 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -22,10 +22,10 @@
)
from api.packet import assess_performance_by_packet
from api.test import verify, write_performance_json
+from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
from framework.params.types import TestPmdParamsDict
-from framework.test_suite import BaseConfig, TestSuite, perf_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 271ad4301c..38ed2234cd 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,10 +19,10 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.linux_session import LinuxSession
from framework.utils import REGEX_FOR_PCI_ADDRESS
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index fa91f7ee2f..263a915745 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,9 +18,9 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.virtual_device import VirtualDevice
from framework.utils import generate_random_packets
diff --git a/dts/tests/TestSuite_uni_pkt.py b/dts/tests/TestSuite_uni_pkt.py
index 222276ce67..d83185d1b2 100644
--- a/dts/tests/TestSuite_uni_pkt.py
+++ b/dts/tests/TestSuite_uni_pkt.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
class TestUniPkt(TestSuite):
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index bdecdb76fd..fd1fc476cc 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,9 +12,9 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
from framework.testbed_model.capability import requires
from framework.testbed_model.linux_session import LinuxSession
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_vlan.py b/dts/tests/TestSuite_vlan.py
index 898673fc86..975e87b128 100644
--- a/dts/tests/TestSuite_vlan.py
+++ b/dts/tests/TestSuite_vlan.py
@@ -23,9 +23,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_VLAN_FILTER)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 2/7] dts: move testbed model from framework to API
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-23 19:04 ` [PATCH v2 1/7] dts: move test suite module " Dean Marx
@ 2026-04-23 19:04 ` Dean Marx
2026-04-23 19:04 ` [PATCH v2 3/7] dts: move exception module " Dean Marx
` (5 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 19:04 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites import modules from testbed model
in the framework. Move this directory to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 +++++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
doc/api/dts/framework.testbed_model.rst | 28 -------------------
...mework.testbed_model.traffic_generator.rst | 16 -----------
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/capabilities.py | 8 +++---
dts/api/packet.py | 14 +++++-----
dts/api/test_suite.py | 4 +--
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 12 ++++----
dts/{framework => api}/testbed_model/cpu.py | 4 +--
.../testbed_model/linux_session.py | 6 ++--
dts/{framework => api}/testbed_model/node.py | 2 +-
.../testbed_model/os_session.py | 8 +++---
dts/{framework => api}/testbed_model/port.py | 2 +-
.../testbed_model/posix_session.py | 6 ++--
.../testbed_model/topology.py | 6 ++--
.../traffic_generator/__init__.py | 4 +--
.../capturing_traffic_generator.py | 4 +--
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 14 +++++-----
.../traffic_generator/traffic_generator.py | 8 +++---
.../testbed_model/traffic_generator/trex.py | 16 +++++------
.../testbed_model/virtual_device.py | 0
dts/framework/config/node.py | 16 +++++------
dts/framework/config/test_run.py | 2 +-
dts/framework/context.py | 10 +++----
dts/framework/params/eal.py | 6 ++--
dts/framework/params/types.py | 6 ++--
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 8 +++---
dts/framework/remote_session/dpdk_shell.py | 2 +-
.../remote_session/interactive_shell.py | 2 +-
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 2 +-
dts/framework/test_run.py | 16 +++++------
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 8 +++---
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 6 ++--
55 files changed, 165 insertions(+), 165 deletions(-)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (98%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
rename dts/{framework => api}/testbed_model/linux_session.py (99%)
rename dts/{framework => api}/testbed_model/node.py (99%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (98%)
rename dts/{framework => api}/testbed_model/posix_session.py (99%)
rename dts/{framework => api}/testbed_model/topology.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (95%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (91%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (96%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
diff --git a/doc/api/dts/framework.testbed_model.capability.rst b/doc/api/dts/api.testbed_model.capability.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.capability.rst
rename to doc/api/dts/api.testbed_model.capability.rst
index fab91cad83..88e396dddb 100644
--- a/doc/api/dts/framework.testbed_model.capability.rst
+++ b/doc/api/dts/api.testbed_model.capability.rst
@@ -3,6 +3,6 @@
capability - Testbed Capabilities
=================================
-.. automodule:: framework.testbed_model.capability
+.. automodule:: api.testbed_model.capability
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.cpu.rst b/doc/api/dts/api.testbed_model.cpu.rst
similarity index 78%
rename from doc/api/dts/framework.testbed_model.cpu.rst
rename to doc/api/dts/api.testbed_model.cpu.rst
index 997f2a9795..dbbb29480a 100644
--- a/doc/api/dts/framework.testbed_model.cpu.rst
+++ b/doc/api/dts/api.testbed_model.cpu.rst
@@ -3,6 +3,6 @@
cpu - CPU Representation and Utilities
======================================
-.. automodule:: framework.testbed_model.cpu
+.. automodule:: api.testbed_model.cpu
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.node.rst b/doc/api/dts/api.testbed_model.node.rst
similarity index 71%
rename from doc/api/dts/framework.testbed_model.node.rst
rename to doc/api/dts/api.testbed_model.node.rst
index 23c6c46a00..15f522e5f7 100644
--- a/doc/api/dts/framework.testbed_model.node.rst
+++ b/doc/api/dts/api.testbed_model.node.rst
@@ -3,6 +3,6 @@
node - Base Node
================
-.. automodule:: framework.testbed_model.node
+.. automodule:: api.testbed_model.node
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.os_session.rst b/doc/api/dts/api.testbed_model.os_session.rst
similarity index 76%
rename from doc/api/dts/framework.testbed_model.os_session.rst
rename to doc/api/dts/api.testbed_model.os_session.rst
index ecfb352311..e7e3f9894f 100644
--- a/doc/api/dts/framework.testbed_model.os_session.rst
+++ b/doc/api/dts/api.testbed_model.os_session.rst
@@ -3,6 +3,6 @@
os\_session - OS-aware Remote Session ABC
=========================================
-.. automodule:: framework.testbed_model.os_session
+.. automodule:: api.testbed_model.os_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.port.rst b/doc/api/dts/api.testbed_model.port.rst
similarity index 77%
rename from doc/api/dts/framework.testbed_model.port.rst
rename to doc/api/dts/api.testbed_model.port.rst
index fdb7ca8a1d..d64501aef0 100644
--- a/doc/api/dts/framework.testbed_model.port.rst
+++ b/doc/api/dts/api.testbed_model.port.rst
@@ -3,7 +3,7 @@
port - NIC Port Representation
==============================
-.. automodule:: framework.testbed_model.port
+.. automodule:: api.testbed_model.port
:members:
:show-inheritance:
:noindex:
diff --git a/doc/api/dts/framework.testbed_model.posix_session.rst b/doc/api/dts/api.testbed_model.posix_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.posix_session.rst
rename to doc/api/dts/api.testbed_model.posix_session.rst
index e65585fd85..9f0e9ff18d 100644
--- a/doc/api/dts/framework.testbed_model.posix_session.rst
+++ b/doc/api/dts/api.testbed_model.posix_session.rst
@@ -3,6 +3,6 @@
posix\_session - Posix Remote Session
=====================================
-.. automodule:: framework.testbed_model.posix_session
+.. automodule:: api.testbed_model.posix_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.rst b/doc/api/dts/api.testbed_model.rst
new file mode 100644
index 0000000000..5e2e5189b2
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.rst
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+testbed\_model - Testbed Modelling Package
+==========================================
+
+.. automodule:: api.testbed_model
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 2
+
+ api.testbed_model.traffic_generator
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.os_session
+ api.testbed_model.linux_session
+ api.testbed_model.posix_session
+ api.testbed_model.node
+ api.testbed_model.capability
+ api.testbed_model.cpu
+ api.testbed_model.port
+ api.testbed_model.topology
+ api.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.topology.rst b/doc/api/dts/api.testbed_model.topology.rst
similarity index 73%
rename from doc/api/dts/framework.testbed_model.topology.rst
rename to doc/api/dts/api.testbed_model.topology.rst
index 496f2a895f..bb63fe38dc 100644
--- a/doc/api/dts/framework.testbed_model.topology.rst
+++ b/doc/api/dts/api.testbed_model.topology.rst
@@ -3,6 +3,6 @@
topology - Testbed Topology
===========================
-.. automodule:: framework.testbed_model.topology
+.. automodule:: api.testbed_model.topology
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
similarity index 68%
rename from doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
index 29fa834042..cfe03201a7 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
@@ -3,6 +3,6 @@
capturing\_traffic\_generator - Base Capturing TG ABC
=====================================================
-.. automodule:: framework.testbed_model.traffic_generator.capturing_traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.capturing_traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.rst
new file mode 100644
index 0000000000..311bdcf6b9
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.traffic_generator.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+traffic\_generator Subpackage
+=============================
+
+.. automodule:: api.testbed_model.traffic_generator
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.traffic_generator.traffic_generator
+ api.testbed_model.traffic_generator.capturing_traffic_generator
+ api.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
similarity index 70%
rename from doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
index df78ac9514..949bb66632 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
@@ -3,6 +3,6 @@
scapy - Capturing Traffic Generator
===================================
-.. automodule:: framework.testbed_model.traffic_generator.scapy
+.. automodule:: api.testbed_model.traffic_generator.scapy
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
similarity index 65%
rename from doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
index bfec728dee..1045e534b5 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
@@ -3,6 +3,6 @@
traffic\_generator - Base TG ABC
================================
-.. automodule:: framework.testbed_model.traffic_generator.traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.virtual_device.rst b/doc/api/dts/api.testbed_model.virtual_device.rst
similarity index 72%
rename from doc/api/dts/framework.testbed_model.virtual_device.rst
rename to doc/api/dts/api.testbed_model.virtual_device.rst
index a6b0420e75..97adc895f6 100644
--- a/doc/api/dts/framework.testbed_model.virtual_device.rst
+++ b/doc/api/dts/api.testbed_model.virtual_device.rst
@@ -3,6 +3,6 @@
virtual\_device - Virtual Devices
=================================
-.. automodule:: framework.testbed_model.virtual_device
+.. automodule:: api.testbed_model.virtual_device
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.rst b/doc/api/dts/framework.testbed_model.rst
deleted file mode 100644
index f283178f6a..0000000000
--- a/doc/api/dts/framework.testbed_model.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-testbed\_model - Testbed Modelling Package
-==========================================
-
-.. automodule:: framework.testbed_model
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 2
-
- framework.testbed_model.traffic_generator
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.os_session
- framework.testbed_model.linux_session
- framework.testbed_model.posix_session
- framework.testbed_model.node
- framework.testbed_model.capability
- framework.testbed_model.cpu
- framework.testbed_model.port
- framework.testbed_model.topology
- framework.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.rst b/doc/api/dts/framework.testbed_model.traffic_generator.rst
deleted file mode 100644
index 24c250ee3a..0000000000
--- a/doc/api/dts/framework.testbed_model.traffic_generator.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-traffic\_generator Subpackage
-=============================
-
-.. automodule:: framework.testbed_model.traffic_generator
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.traffic_generator.traffic_generator
- framework.testbed_model.traffic_generator.capturing_traffic_generator
- framework.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index b9338d6b11..527cc8ce53 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -16,7 +16,7 @@ Packages
tests
api
- framework.testbed_model
+ api.testbed_model
framework.remote_session
framework.params
framework.config
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 24a2b05063..025c87bbfa 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -47,10 +47,10 @@
from paramiko import SFTPClient, SFTPFile
from typing_extensions import Buffer
+from api.testbed_model.node import Node, NodeIdentifier, get_node
from framework.exception import InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node, NodeIdentifier, get_node
TextMode: TypeAlias = (
Literal["r", "r+", "w", "w+", "a", "a+", "x", "x+"]
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 8569cacbd2..a4d6b2b424 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -23,7 +23,7 @@
.. code:: python
from api.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires_link_topology
+ from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -34,7 +34,7 @@ def hello_world_single_core(self):
.. code:: python
from api.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires_nic_capability
+ from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -235,7 +235,7 @@ def requires_link_topology(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import TopologyCapability
+ from api.testbed_model.capability import TopologyCapability
def add_required_topology(
test_case_or_suite: type["TestProtocol"],
@@ -258,7 +258,7 @@ def requires_nic_capability(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import DecoratedNicCapability
+ from api.testbed_model.capability import DecoratedNicCapability
def add_required_capability(
test_case_or_suite: type["TestProtocol"],
diff --git a/dts/api/packet.py b/dts/api/packet.py
index fc2be931fe..d30d455485 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -28,14 +28,14 @@
from scapy.packet import Packet, Padding, raw
from api.test import fail, log_debug
-from framework.context import get_ctx
-from framework.exception import InternalError
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
+from framework.context import get_ctx
+from framework.exception import InternalError
from framework.utils import get_packet_summaries
@@ -82,10 +82,10 @@ def send_packets_and_capture(
Returns:
A list of received packets.
"""
- from framework.context import get_ctx
- from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+ from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
+ from framework.context import get_ctx
assert isinstance(
get_ctx().func_tg, CapturingTrafficGenerator
@@ -340,7 +340,7 @@ def assess_performance_by_packet(
Returns:
Performance statistics of the generated test.
"""
- from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+ from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficGenerator,
)
diff --git a/dts/api/test_suite.py b/dts/api/test_suite.py
index f107b1cd2c..ebb07a9cae 100644
--- a/dts/api/test_suite.py
+++ b/dts/api/test_suite.py
@@ -29,11 +29,11 @@
from scapy.packet import Packet
from typing_extensions import Self
+from api.testbed_model.capability import TestProtocol
+from api.testbed_model.topology import Topology
from framework.config.common import FrozenModel
from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
-from framework.testbed_model.capability import TestProtocol
-from framework.testbed_model.topology import Topology
from framework.utils import to_pascal_case
if TYPE_CHECKING:
diff --git a/dts/framework/testbed_model/__init__.py b/dts/api/testbed_model/__init__.py
similarity index 100%
rename from dts/framework/testbed_model/__init__.py
rename to dts/api/testbed_model/__init__.py
diff --git a/dts/framework/testbed_model/capability.py b/dts/api/testbed_model/capability.py
similarity index 98%
rename from dts/framework/testbed_model/capability.py
rename to dts/api/testbed_model/capability.py
index 39f6f3c0df..95583261d8 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -26,7 +26,7 @@
.. code:: python
from api.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires
+ from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -37,7 +37,7 @@ def hello_world_single_core(self):
.. code:: python
from api.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires
+ from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -64,11 +64,11 @@ def test_scatter_mbuf_2048(self):
from typing_extensions import Self
from api.capabilities import LinkTopology, NicCapability
-from framework.exception import ConfigurationError, InternalError, SkippedTestException
+from api.exception import ConfigurationError, InternalError, SkippedTestException
+from api.testbed_model.node import Node
+from api.testbed_model.port import DriverKind
+from api.testbed_model.topology import Topology
from framework.logger import get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import DriverKind
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
from api.test_suite import TestCase
diff --git a/dts/framework/testbed_model/cpu.py b/dts/api/testbed_model/cpu.py
similarity index 99%
rename from dts/framework/testbed_model/cpu.py
rename to dts/api/testbed_model/cpu.py
index 6e2ecca080..ee754f5844 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/api/testbed_model/cpu.py
@@ -24,12 +24,12 @@
from dataclasses import dataclass
from enum import auto, unique
-from framework.utils import StrEnum, expand_range
+from api.utils import StrEnum, expand_range
@unique
class Architecture(StrEnum):
- r"""The supported architectures of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported architectures of :class:`~api.testbed_model.node.Node`\s."""
#:
i686 = auto()
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
similarity index 99%
rename from dts/framework/testbed_model/linux_session.py
rename to dts/api/testbed_model/linux_session.py
index ee943462c2..7307b2abe2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -18,13 +18,13 @@
from typing_extensions import NotRequired
-from framework.exception import (
+from api.exception import (
ConfigurationError,
InternalError,
RemoteCommandExecutionError,
)
-from framework.testbed_model.port import PortInfo
-from framework.utils import expand_range
+from api.testbed_model.port import PortInfo
+from api.utils import expand_range
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/node.py b/dts/api/testbed_model/node.py
similarity index 99%
rename from dts/framework/testbed_model/node.py
rename to dts/api/testbed_model/node.py
index 67a96ef4e5..4f42bf6aeb 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -17,11 +17,11 @@
from pathlib import PurePath
from typing import Literal, TypeAlias
+from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
-from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
diff --git a/dts/framework/testbed_model/os_session.py b/dts/api/testbed_model/os_session.py
similarity index 99%
rename from dts/framework/testbed_model/os_session.py
rename to dts/api/testbed_model/os_session.py
index 2c267afed1..b1e0538ac9 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/api/testbed_model/os_session.py
@@ -29,12 +29,12 @@
from enum import Flag, auto
from pathlib import Path, PurePath, PurePosixPath
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.remote_session.interactive_remote_session import InteractiveRemoteSession
from framework.remote_session.remote_session import CommandResult, RemoteSession
from framework.settings import SETTINGS
-from framework.utils import MesonArgs, TarCompressionFormat
from .cpu import Architecture, LogicalCore
from .port import Port, PortInfo
@@ -73,11 +73,11 @@ class OSSessionInfo:
Attributes:
os_name: The name of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
os_version: The version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
kernel_version: The kernel version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
"""
os_name: str
diff --git a/dts/framework/testbed_model/port.py b/dts/api/testbed_model/port.py
similarity index 98%
rename from dts/framework/testbed_model/port.py
rename to dts/api/testbed_model/port.py
index d81bc4cda0..aea3e59c25 100644
--- a/dts/framework/testbed_model/port.py
+++ b/dts/api/testbed_model/port.py
@@ -12,8 +12,8 @@
from functools import cached_property
from typing import TYPE_CHECKING, Any, Final, Literal, NamedTuple
+from api.exception import InternalError
from framework.config.node import PortConfig
-from framework.exception import InternalError
if TYPE_CHECKING:
from .node import Node
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/api/testbed_model/posix_session.py
similarity index 99%
rename from dts/framework/testbed_model/posix_session.py
rename to dts/api/testbed_model/posix_session.py
index dec952685a..61c634dad1 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/api/testbed_model/posix_session.py
@@ -16,15 +16,15 @@
from collections.abc import Iterable
from pathlib import Path, PurePath, PurePosixPath
-from framework.exception import DPDKBuildError, RemoteCommandExecutionError
-from framework.settings import SETTINGS
-from framework.utils import (
+from api.exception import DPDKBuildError, RemoteCommandExecutionError
+from api.utils import (
MesonArgs,
TarCompressionFormat,
convert_to_list_of_string,
create_tarball,
extract_tarball,
)
+from framework.settings import SETTINGS
from .cpu import Architecture
from .os_session import FilePermissions, OSSession, OSSessionInfo
diff --git a/dts/framework/testbed_model/topology.py b/dts/api/testbed_model/topology.py
similarity index 98%
rename from dts/framework/testbed_model/topology.py
rename to dts/api/testbed_model/topology.py
index 34862c4d2e..5b8fe03836 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/api/testbed_model/topology.py
@@ -18,9 +18,9 @@
from typing_extensions import Self
from api.capabilities import LinkTopology
-from framework.exception import ConfigurationError, InternalError
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.node import Node, NodeIdentifier
+from api.exception import ConfigurationError, InternalError
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.node import Node, NodeIdentifier
from .port import DriverKind, Port, PortConfig
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/api/testbed_model/traffic_generator/__init__.py
similarity index 95%
rename from dts/framework/testbed_model/traffic_generator/__init__.py
rename to dts/api/testbed_model/traffic_generator/__init__.py
index fca251f534..11fa25448a 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/api/testbed_model/traffic_generator/__init__.py
@@ -14,13 +14,13 @@
and a capturing traffic generator is required.
"""
+from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.exception import ConfigurationError
-from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
similarity index 98%
rename from dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
index 7655751d7e..db274e5e82 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
-from framework.testbed_model.port import Port
-from framework.utils import get_packet_summaries
+from api.testbed_model.port import Port
+from api.utils import get_packet_summaries
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py b/dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
similarity index 100%
rename from dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/api/testbed_model/traffic_generator/scapy.py
similarity index 98%
rename from dts/framework/testbed_model/traffic_generator/scapy.py
rename to dts/api/testbed_model/traffic_generator/scapy.py
index c6e9006205..215c57f93d 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/api/testbed_model/traffic_generator/scapy.py
@@ -25,16 +25,16 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet
+from api.exception import InteractiveSSHSessionDeadError, InternalError
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
-from framework.exception import InteractiveSSHSessionDeadError, InternalError
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
from .capturing_traffic_generator import CapturingTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/api/testbed_model/traffic_generator/traffic_generator.py
similarity index 91%
rename from dts/framework/testbed_model/traffic_generator/traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/traffic_generator.py
index cdda5a7c08..5fd68e5144 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/traffic_generator.py
@@ -13,11 +13,11 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
from framework.config.test_run import TrafficGeneratorConfig
from framework.logger import DTSLogger, get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
class TrafficGenerator(ABC):
@@ -25,7 +25,7 @@ class TrafficGenerator(ABC):
Exposes the common public methods of all traffic generators and defines private methods
that must implement the traffic generation logic in subclasses. This class also extends from
- :class:`framework.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
+ :class:`api.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
from multiple classes to fulfil the traffic generating functionality without breaking
single inheritance.
"""
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/api/testbed_model/traffic_generator/trex.py
similarity index 96%
rename from dts/framework/testbed_model/traffic_generator/trex.py
rename to dts/api/testbed_model/traffic_generator/trex.py
index 22cd20dea9..d97ed934c9 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/api/testbed_model/traffic_generator/trex.py
@@ -11,19 +11,19 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node, create_session
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
+ PerformanceTrafficGenerator,
+ PerformanceTrafficStats,
+)
+from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node, create_session
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
- PerformanceTrafficGenerator,
- PerformanceTrafficStats,
-)
-from framework.utils import StrEnum
@dataclass(slots=True)
diff --git a/dts/framework/testbed_model/virtual_device.py b/dts/api/testbed_model/virtual_device.py
similarity index 100%
rename from dts/framework/testbed_model/virtual_device.py
rename to dts/api/testbed_model/virtual_device.py
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 792290f11f..63062a31b5 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -21,7 +21,7 @@
@unique
class OS(StrEnum):
- r"""The supported operating systems of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported operating systems of :class:`~api.testbed_model.node.Node`\s."""
#:
linux = auto()
@@ -32,7 +32,7 @@ class OS(StrEnum):
class HugepageConfiguration(FrozenModel):
- r"""The hugepage configuration of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The hugepage configuration of :class:`~api.testbed_model.node.Node`\s."""
#: The number of hugepages to allocate.
number_of: int
@@ -41,7 +41,7 @@ class HugepageConfiguration(FrozenModel):
class PortConfig(FrozenModel):
- r"""The port configuration of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The port configuration of :class:`~api.testbed_model.node.Node`\s."""
#: An identifier for the port. May contain letters, digits, underscores, hyphens and spaces.
name: str = Field(pattern=REGEX_FOR_IDENTIFIER)
@@ -54,17 +54,17 @@ class PortConfig(FrozenModel):
class NodeConfiguration(FrozenModel):
- r"""The configuration of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The configuration of :class:`~api.testbed_model.node.Node`\s."""
- #: The name of the :class:`~framework.testbed_model.node.Node`.
+ #: The name of the :class:`~api.testbed_model.node.Node`.
name: str = Field(pattern=REGEX_FOR_IDENTIFIER)
- #: The hostname of the :class:`~framework.testbed_model.node.Node`. Can also be an IP address.
+ #: The hostname of the :class:`~api.testbed_model.node.Node`. Can also be an IP address.
hostname: str
- #: The name of the user used to connect to the :class:`~framework.testbed_model.node.Node`.
+ #: The name of the user used to connect to the :class:`~api.testbed_model.node.Node`.
user: str
#: The password of the user. The use of passwords is heavily discouraged, please use SSH keys.
password: str | None = None
- #: The operating system of the :class:`~framework.testbed_model.node.Node`.
+ #: The operating system of the :class:`~api.testbed_model.node.Node`.
os: OS
#: An optional hugepage configuration.
hugepages: HugepageConfiguration | None = Field(None, alias="hugepages_2mb")
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 75737418a8..2c16a712eb 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -38,7 +38,7 @@
@unique
class Compiler(StrEnum):
- r"""The supported compilers of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported compilers of :class:`~api.testbed_model.node.Node`\s."""
#:
gcc = auto()
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 86745ab56b..618eb3dda7 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -8,18 +8,18 @@
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
+from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
+from api.testbed_model.node import Node
+from api.testbed_model.topology import Topology
from framework.exception import InternalError
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
-from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
from api.test_suite import TestCase, TestSuite
+ from api.testbed_model.capability import TestProtocol
+ from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
- from framework.testbed_model.capability import TestProtocol
- from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
P = ParamSpec("P")
diff --git a/dts/framework/params/eal.py b/dts/framework/params/eal.py
index e84a20f02f..86bfd3fcc6 100644
--- a/dts/framework/params/eal.py
+++ b/dts/framework/params/eal.py
@@ -6,12 +6,12 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.virtual_device import VirtualDevice
from framework.params import Params, Switch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.virtual_device import VirtualDevice
if TYPE_CHECKING:
- from framework.testbed_model.port import Port
+ from api.testbed_model.port import Port
def _port_to_pci(port: "Port") -> str:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 3c7650474c..f2fa69f8b8 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -32,6 +32,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.port import Port
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -54,9 +57,6 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TxUDPPortPair,
)
from framework.params import Switch, YesNoSwitch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.port import Port
-from framework.testbed_model.virtual_device import VirtualDevice
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index c3b02dcc62..4181c20e43 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -30,12 +30,12 @@
from typing_extensions import Self
+from api.testbed_model.node import Node
from framework.context import get_ctx
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
-from framework.testbed_model.node import Node
P = TypeVar("P", bound=Params)
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index c3575cfcaf..c955f4def2 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,6 +13,10 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
+from api.testbed_model.node import Node
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.virtual_device import VirtualDevice
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -29,10 +33,6 @@
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
-from framework.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
-from framework.testbed_model.node import Node
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.virtual_device import VirtualDevice
from framework.utils import MesonArgs, TarCompressionFormat
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 269c2cada4..ac89ec0459 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -10,13 +10,13 @@
from abc import ABC, abstractmethod
from pathlib import PurePath
+from api.testbed_model.cpu import LogicalCoreList
from framework.context import get_ctx
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
)
-from framework.testbed_model.cpu import LogicalCoreList
def compute_eal_params(
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index a65cbce209..23d05fbdff 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,6 +29,7 @@
from paramiko import Channel, channel
from typing_extensions import Self
+from api.testbed_model.node import Node
from framework.context import get_ctx
from framework.exception import (
InteractiveCommandExecutionError,
@@ -38,7 +39,6 @@
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node
P = ParamSpec("P")
T = TypeVar("T", bound="InteractiveShell")
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index 6ea4749ff4..0e245a515b 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -12,10 +12,10 @@
import sys
import textwrap
+from api.testbed_model.node import Node
from framework.config.common import ValidationContext
from framework.exception import ConfigurationError
from framework.test_run import TestRun
-from framework.testbed_model.node import Node
from .config import Configuration, load_config
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 21faa55dc1..e2efff8681 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -35,9 +35,9 @@
)
from typing_extensions import OrderedDict
+from api.testbed_model.os_session import OSSessionInfo
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
-from framework.testbed_model.os_session import OSSessionInfo
from .exception import DTSError, ErrorSeverity, InternalError
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 037ae6c9fb..14c04d1ce0 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -107,6 +107,14 @@
from typing import ClassVar, Protocol, Union
from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
+from api.testbed_model.capability import (
+ Capability,
+ get_supported_capabilities,
+ test_if_supported,
+)
+from api.testbed_model.node import Node
+from api.testbed_model.topology import PortLink, Topology
+from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
@@ -114,14 +122,6 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.testbed_model.capability import (
- Capability,
- get_supported_capabilities,
- test_if_supported,
-)
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import PortLink, Topology
-from framework.testbed_model.traffic_generator import create_traffic_generator
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index 05bfe951fd..31e69c0de9 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -12,8 +12,8 @@
)
from api.test import verify
from api.test_suite import TestSuite, func_test
+from api.testbed_model.port import Port
from api.testpmd import TestPmd
-from framework.testbed_model.port import Port
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 3f046ad87e..83ce94d4df 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -31,9 +31,9 @@
)
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, crypto_test
+from api.testbed_model.virtual_device import VirtualDevice
from framework.context import get_ctx
from framework.exception import SkippedTestException
-from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 44fee58775..63f771d594 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -19,10 +19,10 @@
send_packets_and_capture,
)
from api.test_suite import TestSuite, func_test
+from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from framework.context import filter_cores
-from framework.testbed_model.cpu import LogicalCoreCount
from framework.utils import generate_random_packets
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index d37796954d..fd5cef5268 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -37,14 +37,14 @@
)
from api.test import verify
from api.test_suite import TestSuite, func_test
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from api.testpmd import TestPmd
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 38ed2234cd..157dec7585 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -20,10 +20,10 @@
)
from api.test import verify
from api.test_suite import TestSuite, func_test
+from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.testbed_model.linux_session import LinuxSession
from framework.utils import REGEX_FOR_PCI_ADDRESS
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index 263a915745..0696933053 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -19,9 +19,9 @@
send_packets_and_capture,
)
from api.test_suite import TestSuite, func_test
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
-from framework.testbed_model.virtual_device import VirtualDevice
from framework.utils import generate_random_packets
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index fd1fc476cc..2c10478df3 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -13,11 +13,11 @@
from api.packet import send_packets_and_capture
from api.test import log, verify
from api.test_suite import TestSuite, func_test
+from api.testbed_model.capability import requires
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.testbed_model.capability import requires
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.virtual_device import VirtualDevice
class TestVirtioFwd(TestSuite):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 3/7] dts: move exception module from framework to API
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-23 19:04 ` [PATCH v2 1/7] dts: move test suite module " Dean Marx
2026-04-23 19:04 ` [PATCH v2 2/7] dts: move testbed model " Dean Marx
@ 2026-04-23 19:04 ` Dean Marx
2026-04-23 19:04 ` [PATCH v2 4/7] dts: move utils " Dean Marx
` (4 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 19:04 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites currently import the exception module
from the framework in order to catch certain errors during
test execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.exception.rst => api.exception.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 2 +-
dts/api/test.py | 2 +-
dts/api/test_suite.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/config/__init__.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/context.py | 2 +-
dts/framework/parser.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
.../remote_session/interactive_remote_session.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 6 +++---
dts/framework/remote_session/remote_session.py | 4 ++--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 3 +--
dts/framework/test_run.py | 4 ++--
dts/framework/utils.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
| 2 +-
dts/tests/TestSuite_rte_flow.py | 10 +++++-----
26 files changed, 33 insertions(+), 34 deletions(-)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename dts/{framework => api}/exception.py (100%)
diff --git a/doc/api/dts/framework.exception.rst b/doc/api/dts/api.exception.rst
similarity index 77%
rename from doc/api/dts/framework.exception.rst
rename to doc/api/dts/api.exception.rst
index efb47dc5ae..8e6bff5ee7 100644
--- a/doc/api/dts/framework.exception.rst
+++ b/doc/api/dts/api.exception.rst
@@ -3,6 +3,6 @@
exception - Exceptions
======================
-.. automodule:: framework.exception
+.. automodule:: api.exception
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 527cc8ce53..3b9ffab4ce 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -36,7 +36,7 @@ Modules
framework.logger
framework.parser
framework.utils
- framework.exception
+ api.exception
Indices and tables
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 025c87bbfa..02d807241f 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -47,8 +47,8 @@
from paramiko import SFTPClient, SFTPFile
from typing_extensions import Buffer
+from api.exception import InternalError
from api.testbed_model.node import Node, NodeIdentifier, get_node
-from framework.exception import InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index a4fafc3713..c6a220dced 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -22,8 +22,8 @@
ThroughputResults,
VerifyResults,
)
+from api.exception import RemoteCommandExecutionError, SkippedTestException
from framework.context import get_ctx
-from framework.exception import RemoteCommandExecutionError, SkippedTestException
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/framework/exception.py b/dts/api/exception.py
similarity index 100%
rename from dts/framework/exception.py
rename to dts/api/exception.py
diff --git a/dts/api/packet.py b/dts/api/packet.py
index d30d455485..2bf31aa753 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,6 +27,7 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.exception import InternalError
from api.test import fail, log_debug
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -35,7 +36,6 @@
PerformanceTrafficStats,
)
from framework.context import get_ctx
-from framework.exception import InternalError
from framework.utils import get_packet_summaries
diff --git a/dts/api/test.py b/dts/api/test.py
index e17babe0ca..9cad9a9495 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.context import get_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/api/test_suite.py b/dts/api/test_suite.py
index ebb07a9cae..be13485f9b 100644
--- a/dts/api/test_suite.py
+++ b/dts/api/test_suite.py
@@ -29,10 +29,10 @@
from scapy.packet import Packet
from typing_extensions import Self
+from api.exception import ConfigurationError, InternalError
from api.testbed_model.capability import TestProtocol
from api.testbed_model.topology import Topology
from framework.config.common import FrozenModel
-from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.utils import to_pascal_case
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..9498d723d5 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
@@ -55,7 +56,6 @@
VLANOffloadFlag,
)
from framework.context import get_ctx
-from framework.exception import InteractiveCommandExecutionError, InternalError
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index 6f4f9d82f8..3a3580aaf7 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -35,7 +35,7 @@
from pydantic import Field, TypeAdapter, ValidationError, model_validator
from typing_extensions import Self
-from framework.exception import ConfigurationError
+from api.exception import ConfigurationError
from .common import FrozenModel, ValidationContext
from .node import NodeConfiguration
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 2c16a712eb..1b051fbadf 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -27,7 +27,7 @@
)
from typing_extensions import TYPE_CHECKING, Self
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 618eb3dda7..7ed4cc5665 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -8,10 +8,10 @@
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
+from api.exception import InternalError
from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
from api.testbed_model.node import Node
from api.testbed_model.topology import Topology
-from framework.exception import InternalError
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 3075c36857..ebf470ad30 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -15,7 +15,7 @@
from typing_extensions import Self
-from framework.exception import InternalError
+from api.exception import InternalError
class ParserFn(TypedDict):
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index c955f4def2..91173e0796 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,6 +13,7 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.exception import ConfigurationError, RemoteFileNotFoundError
from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
@@ -29,7 +30,6 @@
RemoteDPDKTreeLocation,
)
from framework.context import get_ctx
-from framework.exception import ConfigurationError, RemoteFileNotFoundError
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/interactive_remote_session.py b/dts/framework/remote_session/interactive_remote_session.py
index c8156b4345..04f45e0df8 100644
--- a/dts/framework/remote_session/interactive_remote_session.py
+++ b/dts/framework/remote_session/interactive_remote_session.py
@@ -15,8 +15,8 @@
SSHException,
)
+from api.exception import SSHConnectionError
from framework.config.node import NodeConfiguration
-from framework.exception import SSHConnectionError
from framework.logger import DTSLogger
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index 23d05fbdff..15743949e7 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,13 +29,13 @@
from paramiko import Channel, channel
from typing_extensions import Self
-from api.testbed_model.node import Node
-from framework.context import get_ctx
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from api.testbed_model.node import Node
+from framework.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py
index 158325bb7f..f49966070f 100644
--- a/dts/framework/remote_session/remote_session.py
+++ b/dts/framework/remote_session/remote_session.py
@@ -24,13 +24,13 @@
SSHException,
)
-from framework.config.node import NodeConfiguration
-from framework.exception import (
+from api.exception import (
RemoteCommandExecutionError,
SSHConnectionError,
SSHSessionDeadError,
SSHTimeoutError,
)
+from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.settings import SETTINGS
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index 0e245a515b..29be7b80fe 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -12,9 +12,9 @@
import sys
import textwrap
+from api.exception import ConfigurationError
from api.testbed_model.node import Node
from framework.config.common import ValidationContext
-from framework.exception import ConfigurationError
from framework.test_run import TestRun
from .config import Configuration, load_config
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index e2efff8681..5f945163ce 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -35,12 +35,11 @@
)
from typing_extensions import OrderedDict
+from api.exception import DTSError, ErrorSeverity, InternalError
from api.testbed_model.os_session import OSSessionInfo
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
-from .exception import DTSError, ErrorSeverity, InternalError
-
class Result(IntEnum):
"""The possible states that a setup, a teardown or a test case may end up in."""
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 14c04d1ce0..9972d26b04 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,6 +106,7 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from api.testbed_model.capability import (
Capability,
@@ -117,7 +118,6 @@
from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
@@ -136,7 +136,7 @@ class TestRun:
If an error occurs, the current stage is aborted, the error is recorded, everything in
the inner stages is marked as blocked and the run continues in the next iteration
of the same stage. The return code is the highest `severity` of all
- :class:`~.framework.exception.DTSError`\s.
+ :class:`~.api.exception.DTSError`\s.
Example:
An error occurs in a test suite setup. The current test suite is aborted,
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 9917ffbfaa..28e344871a 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -26,7 +26,7 @@
from scapy.layers.inet import IP, TCP, UDP, Ether
from scapy.packet import Packet
-from .exception import InternalError
+from api.exception import InternalError
REGEX_FOR_PCI_ADDRESS: str = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}"
_REGEX_FOR_COLON_OR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}[:-]){5}[\da-fA-F]{2}"
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 83ce94d4df..fb6fda3bac 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -29,11 +29,11 @@
from api.cryptodev.types import (
CryptodevResults,
)
+from api.exception import SkippedTestException
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
from framework.context import get_ctx
-from framework.exception import SkippedTestException
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index e0ef1f447a..24584c7d60 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -35,12 +35,12 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.exception import InteractiveCommandExecutionError
def setup_and_teardown_test(
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index 98c12459f6..eb1413f336 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -23,11 +23,11 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.exception import InteractiveCommandExecutionError
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 8777d446cd..dae90ee2d5 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -20,6 +20,7 @@
requires_link_topology,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, func_test
@@ -30,7 +31,6 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
-from framework.exception import InteractiveCommandExecutionError
from framework.utils import StrEnum
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 3d74decb11..8c5c59edec 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -21,16 +21,16 @@
from scapy.packet import Packet, Raw
from api.capabilities import NicCapability, requires_nic_capability
+from api.exception import (
+ InteractiveCommandExecutionError,
+ SkippedTestException,
+ TestCaseVerifyError,
+)
from api.packet import send_packet_and_capture
from api.test import fail, log, verify
from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import FlowRule
-from framework.exception import (
- InteractiveCommandExecutionError,
- SkippedTestException,
- TestCaseVerifyError,
-)
@dataclass
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 4/7] dts: move utils from framework to API
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
` (2 preceding siblings ...)
2026-04-23 19:04 ` [PATCH v2 3/7] dts: move exception module " Dean Marx
@ 2026-04-23 19:04 ` Dean Marx
2026-04-23 19:04 ` [PATCH v2 5/7] dts: move context " Dean Marx
` (3 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 19:04 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The utils module is used to generate a set of random
packets in certain test suites. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/config.py | 2 +-
dts/api/packet.py | 2 +-
dts/api/test_suite.py | 2 +-
dts/api/testpmd/config.py | 2 +-
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 0
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
| 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
15 files changed, 14 insertions(+), 14 deletions(-)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
rename dts/{framework => api}/utils.py (100%)
diff --git a/doc/api/dts/framework.utils.rst b/doc/api/dts/api.utils.rst
similarity index 80%
rename from doc/api/dts/framework.utils.rst
rename to doc/api/dts/api.utils.rst
index cc06d4c3c3..0b4baff1b5 100644
--- a/doc/api/dts/framework.utils.rst
+++ b/doc/api/dts/api.utils.rst
@@ -3,6 +3,6 @@
utils - Various Utilities
=========================
-.. automodule:: framework.utils
+.. automodule:: api.utils
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 3b9ffab4ce..ced42ac241 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -35,7 +35,7 @@ Modules
framework.context
framework.logger
framework.parser
- framework.utils
+ api.utils
api.exception
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index 69ff7aa59a..a88e70d45c 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.utils import StrEnum
from framework.params import Params, Switch
from framework.params.eal import EalParams
-from framework.utils import StrEnum
Silent = Literal[""]
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 2bf31aa753..2be00ad48a 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -35,8 +35,8 @@
from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
+from api.utils import get_packet_summaries
from framework.context import get_ctx
-from framework.utils import get_packet_summaries
def send_packet_and_capture(
diff --git a/dts/api/test_suite.py b/dts/api/test_suite.py
index be13485f9b..7feb35a9f8 100644
--- a/dts/api/test_suite.py
+++ b/dts/api/test_suite.py
@@ -32,9 +32,9 @@
from api.exception import ConfigurationError, InternalError
from api.testbed_model.capability import TestProtocol
from api.testbed_model.topology import Topology
+from api.utils import to_pascal_case
from framework.config.common import FrozenModel
from framework.logger import DTSLogger, get_dts_logger
-from framework.utils import to_pascal_case
if TYPE_CHECKING:
from framework.context import Context
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index e71a3e1ef0..8b688834ee 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,6 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
+from api.utils import StrEnum
from framework.params import (
Params,
Switch,
@@ -24,7 +25,6 @@
str_from_flag_value,
)
from framework.params.eal import EalParams
-from framework.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..5c847b4bd6 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -15,8 +15,8 @@
from typing_extensions import Self
+from api.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
from framework.parser import ParserFn, TextParser
-from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
RxTxLiteralSwitch = Literal["rx", "tx"]
diff --git a/dts/framework/utils.py b/dts/api/utils.py
similarity index 100%
rename from dts/framework/utils.py
rename to dts/api/utils.py
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 63062a31b5..d7122d13d8 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -14,7 +14,7 @@
from pydantic import Field, model_validator
from typing_extensions import Self
-from framework.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
+from api.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
from .common import FrozenModel
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 1b051fbadf..10901c740d 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -28,7 +28,7 @@
from typing_extensions import TYPE_CHECKING, Self
from api.exception import InternalError
-from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
+from api.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 91173e0796..1a4fec8ec9 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -18,6 +18,7 @@
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
from api.testbed_model.virtual_device import VirtualDevice
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -33,7 +34,6 @@
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
-from framework.utils import MesonArgs, TarCompressionFormat
@dataclass(slots=True, frozen=True)
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 63f771d594..0b0b7bc931 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -22,8 +22,8 @@
from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
+from api.utils import generate_random_packets
from framework.context import filter_cores
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index dae90ee2d5..162e08ccbc 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -31,7 +31,7 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
-from framework.utils import StrEnum
+from api.utils import StrEnum
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 157dec7585..b3eb325fc0 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -22,9 +22,9 @@
from api.test_suite import TestSuite, func_test
from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
+from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.utils import REGEX_FOR_PCI_ADDRESS
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index 0696933053..05a6d3aa18 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -22,7 +22,7 @@
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
-from framework.utils import generate_random_packets
+from api.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 5/7] dts: move context from framework to API
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
` (3 preceding siblings ...)
2026-04-23 19:04 ` [PATCH v2 4/7] dts: move utils " Dean Marx
@ 2026-04-23 19:04 ` Dean Marx
2026-04-23 19:04 ` [PATCH v2 6/7] dts: move params directory " Dean Marx
` (2 subsequent siblings)
7 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 19:04 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
A couple test suites import and get the run context
during execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.context.rst => api.context.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/{framework => api}/context.py | 0
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 4 ++--
dts/api/test.py | 2 +-
dts/api/test_suite.py | 4 ++--
dts/api/testbed_model/node.py | 2 +-
dts/api/testbed_model/topology.py | 14 +++++++-------
dts/api/testpmd/__init__.py | 2 +-
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/framework/test_run.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
18 files changed, 25 insertions(+), 25 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename dts/{framework => api}/context.py (100%)
diff --git a/doc/api/dts/framework.context.rst b/doc/api/dts/api.context.rst
similarity index 80%
rename from doc/api/dts/framework.context.rst
rename to doc/api/dts/api.context.rst
index 925c160360..65b4ab9a47 100644
--- a/doc/api/dts/framework.context.rst
+++ b/doc/api/dts/api.context.rst
@@ -3,6 +3,6 @@
context - DTS execution context
===============================
-.. automodule:: framework.context
+.. automodule:: api.context
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index ced42ac241..7c282bbba1 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -32,7 +32,7 @@ Modules
api.test_suite
framework.test_result
framework.settings
- framework.context
+ api.context
framework.logger
framework.parser
api.utils
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 02d807241f..74a8ac667f 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -86,7 +86,7 @@ def make_file_path(
path /= custom_path
else:
- from framework.context import get_ctx
+ from api.context import get_ctx
try:
ctx = get_ctx()
diff --git a/dts/framework/context.py b/dts/api/context.py
similarity index 100%
rename from dts/framework/context.py
rename to dts/api/context.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index c6a220dced..67dcb02130 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -23,7 +23,7 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 2be00ad48a..393fee542b 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -36,7 +36,7 @@
PerformanceTrafficStats,
)
from api.utils import get_packet_summaries
-from framework.context import get_ctx
+from api.context import get_ctx
def send_packet_and_capture(
@@ -85,7 +85,7 @@ def send_packets_and_capture(
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
- from framework.context import get_ctx
+ from api.context import get_ctx
assert isinstance(
get_ctx().func_tg, CapturingTrafficGenerator
diff --git a/dts/api/test.py b/dts/api/test.py
index 9cad9a9495..a1f2326075 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -11,7 +11,7 @@
from api.artifact import Artifact
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger
diff --git a/dts/api/test_suite.py b/dts/api/test_suite.py
index 7feb35a9f8..0822f9bfe5 100644
--- a/dts/api/test_suite.py
+++ b/dts/api/test_suite.py
@@ -37,7 +37,7 @@
from framework.logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
- from framework.context import Context
+ from api.context import Context
class BaseConfig(FrozenModel):
@@ -91,7 +91,7 @@ def __init__(self, config: BaseConfig) -> None:
Args:
config: The test suite configuration.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self.config = config
self._ctx = get_ctx()
diff --git a/dts/api/testbed_model/node.py b/dts/api/testbed_model/node.py
index 4f42bf6aeb..40dd7f0666 100644
--- a/dts/api/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -242,7 +242,7 @@ def get_node(node_identifier: NodeIdentifier) -> Node | None:
if node_identifier == "local":
return None
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
if node_identifier == "sut":
diff --git a/dts/api/testbed_model/topology.py b/dts/api/testbed_model/topology.py
index 5b8fe03836..11593d64d5 100644
--- a/dts/api/testbed_model/topology.py
+++ b/dts/api/testbed_model/topology.py
@@ -96,7 +96,7 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
Raises:
InternalError: If the given `node_identifier` is invalid.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
match node_identifier:
@@ -180,7 +180,7 @@ def instantiate_crypto_ports(self) -> None:
Raises:
InternalError: If crypto virtual functions could not be created on a port.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
for port in ctx.sut_node.cryptodevs:
@@ -206,7 +206,7 @@ def instantiate_vf_ports(self) -> None:
Raises:
InternalError: If virtual function creation fails.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -235,7 +235,7 @@ def instantiate_vf_ports(self) -> None:
def delete_vf_ports(self) -> None:
"""Delete virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -246,7 +246,7 @@ def delete_vf_ports(self) -> None:
def delete_crypto_vf_ports(self) -> None:
"""Delete crypto virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -259,7 +259,7 @@ def bind_cryptodevs(self, driver: DriverKind):
Args:
driver: The driver to bind the crypto functions
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
@@ -318,7 +318,7 @@ def _prepare_devbind_script(self) -> None:
Raises:
InternalError: If dpdk-devbind.py could not be found.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
local_script_path = Path("..", "usertools", "dpdk-devbind.py").resolve()
valid_script_path = local_script_path.exists()
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9498d723d5..a528663c21 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -55,7 +55,7 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 4181c20e43..955a7ccdba 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,7 +31,7 @@
from typing_extensions import Self
from api.testbed_model.node import Node
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 1a4fec8ec9..68897bfefe 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -30,7 +30,7 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index ac89ec0459..947f60f75c 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,7 +11,7 @@
from pathlib import PurePath
from api.testbed_model.cpu import LogicalCoreList
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index 15743949e7..6aebef3f45 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -35,7 +35,7 @@
InteractiveSSHTimeoutError,
)
from api.testbed_model.node import Node
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 9972d26b04..914bf9491d 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -117,7 +117,7 @@
from api.testbed_model.topology import PortLink, Topology
from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
-from framework.context import Context, init_ctx
+from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index fb6fda3bac..67ebbc64c2 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -33,7 +33,7 @@
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
-from framework.context import get_ctx
+from api.context import get_ctx
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 0b0b7bc931..f4833340a6 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -23,7 +23,7 @@
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.context import filter_cores
+from api.context import filter_cores
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 6/7] dts: move params directory from framework to API
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
` (4 preceding siblings ...)
2026-04-23 19:04 ` [PATCH v2 5/7] dts: move context " Dean Marx
@ 2026-04-23 19:04 ` Dean Marx
2026-04-23 19:04 ` [PATCH v2 7/7] dts: separate Linux session into interface and logic Dean Marx
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
7 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 19:04 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The params directory is imported in test suites such as
packet capture to use as a base class for dumpcap.
Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.params.eal.rst => api.params.eal.rst} | 2 +-
doc/api/dts/{framework.params.rst => api.params.rst} | 6 +++---
.../{framework.params.types.rst => api.params.types.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/__init__.py | 4 ++--
dts/api/cryptodev/config.py | 4 ++--
dts/api/packet.py | 4 ++--
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 2 +-
dts/{framework => api}/params/types.py | 8 ++++++--
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 9 +++++++--
dts/api/testpmd/config.py | 6 +++---
dts/framework/remote_session/blocking_app.py | 6 +++---
dts/framework/remote_session/dpdk.py | 4 ++--
dts/framework/remote_session/dpdk_shell.py | 4 ++--
dts/framework/remote_session/interactive_shell.py | 4 ++--
dts/framework/test_run.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
22 files changed, 44 insertions(+), 35 deletions(-)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (97%)
rename dts/{framework => api}/params/types.py (97%)
diff --git a/doc/api/dts/framework.params.eal.rst b/doc/api/dts/api.params.eal.rst
similarity index 79%
rename from doc/api/dts/framework.params.eal.rst
rename to doc/api/dts/api.params.eal.rst
index 6999b00233..4531cb1fe1 100644
--- a/doc/api/dts/framework.params.eal.rst
+++ b/doc/api/dts/api.params.eal.rst
@@ -3,6 +3,6 @@
eal - EAL Parameters Modelling
==============================
-.. automodule:: framework.params.eal
+.. automodule:: api.params.eal
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.params.rst b/doc/api/dts/api.params.rst
similarity index 71%
rename from doc/api/dts/framework.params.rst
rename to doc/api/dts/api.params.rst
index d8c6af9667..3ea7f9215e 100644
--- a/doc/api/dts/framework.params.rst
+++ b/doc/api/dts/api.params.rst
@@ -3,7 +3,7 @@
params - Command Line Parameters Modelling
==========================================
-.. automodule:: framework.params
+.. automodule:: api.params
:members:
:show-inheritance:
@@ -11,5 +11,5 @@ params - Command Line Parameters Modelling
:hidden:
:maxdepth: 1
- framework.params.eal
- framework.params.types
+ api.params.eal
+ api.params.types
diff --git a/doc/api/dts/framework.params.types.rst b/doc/api/dts/api.params.types.rst
similarity index 80%
rename from doc/api/dts/framework.params.types.rst
rename to doc/api/dts/api.params.types.rst
index 6d609038be..4754b3a665 100644
--- a/doc/api/dts/framework.params.types.rst
+++ b/doc/api/dts/api.params.types.rst
@@ -3,6 +3,6 @@
params.types - Parameters Modelling Types
=========================================
-.. automodule:: framework.params.types
+.. automodule:: api.params.types
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 7c282bbba1..e89e782ac0 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -18,7 +18,7 @@ Packages
api
api.testbed_model
framework.remote_session
- framework.params
+ api.params
framework.config
Modules
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 67dcb02130..bbfe3622c2 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -14,6 +14,7 @@
from typing_extensions import Unpack
+from api.context import get_ctx
from api.cryptodev.config import CryptoPmdParams, TestType
from api.cryptodev.types import (
CryptodevResults,
@@ -23,11 +24,10 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
- from framework.params.types import CryptoPmdParamsDict
+ from api.params.types import CryptoPmdParamsDict
from pathlib import PurePath
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index a88e70d45c..3420c2fe91 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.params import Params, Switch
+from api.params.eal import EalParams
from api.utils import StrEnum
-from framework.params import Params, Switch
-from framework.params.eal import EalParams
Silent = Literal[""]
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 393fee542b..873b8f0324 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,6 +27,7 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.context import get_ctx
from api.exception import InternalError
from api.test import fail, log_debug
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
@@ -36,7 +37,6 @@
PerformanceTrafficStats,
)
from api.utils import get_packet_summaries
-from api.context import get_ctx
def send_packet_and_capture(
@@ -82,10 +82,10 @@ def send_packets_and_capture(
Returns:
A list of received packets.
"""
+ from api.context import get_ctx
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
- from api.context import get_ctx
assert isinstance(
get_ctx().func_tg, CapturingTrafficGenerator
diff --git a/dts/framework/params/__init__.py b/dts/api/params/__init__.py
similarity index 100%
rename from dts/framework/params/__init__.py
rename to dts/api/params/__init__.py
diff --git a/dts/framework/params/eal.py b/dts/api/params/eal.py
similarity index 97%
rename from dts/framework/params/eal.py
rename to dts/api/params/eal.py
index 86bfd3fcc6..64fa45ae12 100644
--- a/dts/framework/params/eal.py
+++ b/dts/api/params/eal.py
@@ -6,9 +6,9 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.params import Params, Switch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.virtual_device import VirtualDevice
-from framework.params import Params, Switch
if TYPE_CHECKING:
from api.testbed_model.port import Port
diff --git a/dts/framework/params/types.py b/dts/api/params/types.py
similarity index 97%
rename from dts/framework/params/types.py
rename to dts/api/params/types.py
index f2fa69f8b8..280c87ec92 100644
--- a/dts/framework/params/types.py
+++ b/dts/api/params/types.py
@@ -12,8 +12,10 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
params = TestPmdParams(**kwargs)
"""
+from __future__ import annotations
+
from pathlib import PurePath
-from typing import TypedDict
+from typing import TYPE_CHECKING, TypedDict
from api.cryptodev.config import (
AeadAlgName,
@@ -32,6 +34,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
+
+if TYPE_CHECKING:
+ from api.params import Switch, YesNoSwitch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.port import Port
from api.testbed_model.virtual_device import VirtualDevice
@@ -56,7 +61,6 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TXRingParams,
TxUDPPortPair,
)
-from framework.params import Switch, YesNoSwitch
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/api/test.py b/dts/api/test.py
index a1f2326075..03846639ad 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
-from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.context import get_ctx
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index a528663c21..09974048e7 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -14,6 +14,8 @@
testpmd.close()
"""
+from __future__ import annotations
+
import functools
import re
import time
@@ -21,6 +23,7 @@
from enum import Flag
from pathlib import PurePath
from typing import (
+ TYPE_CHECKING,
Any,
Callable,
ClassVar,
@@ -32,7 +35,11 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.context import get_ctx
from api.exception import InteractiveCommandExecutionError, InternalError
+
+if TYPE_CHECKING:
+ from api.params.types import TestPmdParamsDict
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
@@ -55,8 +62,6 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from api.context import get_ctx
-from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
from framework.settings import SETTINGS
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index 8b688834ee..96fe5e79fb 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,8 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
-from api.utils import StrEnum
-from framework.params import (
+from api.params import (
Params,
Switch,
YesNoSwitch,
@@ -24,7 +23,8 @@
modify_str,
str_from_flag_value,
)
-from framework.params.eal import EalParams
+from api.params.eal import EalParams
+from api.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 955a7ccdba..537d937eca 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -30,10 +30,10 @@
from typing_extensions import Self
-from api.testbed_model.node import Node
from api.context import get_ctx
-from framework.params import Params
-from framework.params.eal import EalParams
+from api.params import Params
+from api.params.eal import EalParams
+from api.testbed_model.node import Node
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 68897bfefe..afdf7526d9 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,7 +13,9 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
@@ -30,9 +32,7 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
-from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 947f60f75c..61cc4687f3 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -10,9 +10,9 @@
from abc import ABC, abstractmethod
from pathlib import PurePath
-from api.testbed_model.cpu import LogicalCoreList
from api.context import get_ctx
-from framework.params.eal import EalParams
+from api.params.eal import EalParams
+from api.testbed_model.cpu import LogicalCoreList
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index 6aebef3f45..f7f0669eea 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,15 +29,15 @@
from paramiko import Channel, channel
from typing_extensions import Self
+from api.context import get_ctx
from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from api.params import Params
from api.testbed_model.node import Node
-from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
-from framework.params import Params
from framework.settings import SETTINGS
P = ParamSpec("P")
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 914bf9491d..c133fbecb0 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,6 +106,7 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from api.testbed_model.capability import (
@@ -117,7 +118,6 @@
from api.testbed_model.topology import PortLink, Topology
from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
-from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 67ebbc64c2..d2a6cbab94 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -13,6 +13,7 @@
LinkTopology,
requires_link_topology,
)
+from api.context import get_ctx
from api.cryptodev import Cryptodev
from api.cryptodev.config import (
AeadAlgName,
@@ -33,7 +34,6 @@
from api.test import verify
from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
-from api.context import get_ctx
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index f4833340a6..f237821a04 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -13,6 +13,7 @@
requires_link_topology,
requires_nic_capability,
)
+from api.context import filter_cores
from api.packet import (
get_expected_packets,
match_all_packets,
@@ -23,7 +24,6 @@
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from api.context import filter_cores
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index fd5cef5268..ba67c9e1c6 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -35,6 +35,7 @@
match_all_packets,
send_packets_and_capture,
)
+from api.params import Params
from api.test import verify
from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
@@ -42,7 +43,6 @@
PacketFilteringConfig,
)
from api.testpmd import TestPmd
-from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index f1eb435759..24f2cebf17 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -21,11 +21,11 @@
requires_link_topology,
)
from api.packet import assess_performance_by_packet
+from api.params.types import TestPmdParamsDict
from api.test import verify, write_performance_json
from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
-from framework.params.types import TestPmdParamsDict
class Config(BaseConfig):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v2 7/7] dts: separate Linux session into interface and logic
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
` (5 preceding siblings ...)
2026-04-23 19:04 ` [PATCH v2 6/7] dts: move params directory " Dean Marx
@ 2026-04-23 19:04 ` Dean Marx
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
7 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-23 19:04 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 36200 bytes --]
Separate Linux session into an interface for the API,
and a logical module in the framework.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
...st => api.testbed_model.linux_session.rst} | 2 +-
doc/api/dts/framework.linux_session.rst | 8 +
doc/api/dts/index.rst | 1 +
dts/api/testbed_model/linux_session.py | 366 +----------------
dts/api/testbed_model/node.py | 11 +-
dts/api/testbed_model/os_session.py | 16 +
dts/framework/linux_session.py | 375 ++++++++++++++++++
7 files changed, 424 insertions(+), 355 deletions(-)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
create mode 100644 doc/api/dts/framework.linux_session.rst
create mode 100644 dts/framework/linux_session.py
diff --git a/doc/api/dts/framework.testbed_model.linux_session.rst b/doc/api/dts/api.testbed_model.linux_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.linux_session.rst
rename to doc/api/dts/api.testbed_model.linux_session.rst
index 7567816199..cfe79d8bca 100644
--- a/doc/api/dts/framework.testbed_model.linux_session.rst
+++ b/doc/api/dts/api.testbed_model.linux_session.rst
@@ -3,6 +3,6 @@
linux\_session - Linux Remote Session
=====================================
-.. automodule:: framework.testbed_model.linux_session
+.. automodule:: api.testbed_model.linux_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.linux_session.rst b/doc/api/dts/framework.linux_session.rst
new file mode 100644
index 0000000000..5cb2d04db1
--- /dev/null
+++ b/doc/api/dts/framework.linux_session.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+linux\_session - Linux Remote Session
+=====================================
+
+.. automodule:: framework.linux_session
+ :members:
+ :show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index e89e782ac0..0dbc18b75c 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -37,6 +37,7 @@ Modules
framework.parser
api.utils
api.exception
+ framework.linux_session
Indices and tables
diff --git a/dts/api/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
index 7307b2abe2..da3da4461c 100644
--- a/dts/api/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -2,366 +2,26 @@
# Copyright(c) 2023 PANTHEON.tech s.r.o.
# Copyright(c) 2023 University of New Hampshire
-"""Linux OS translator.
+"""Linux OS session interface.
-Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
-compliant with POSIX standards, so this module only implements the parts that aren't.
-This intermediate module implements the common parts of mostly POSIX compliant distributions.
+Extends the base :class:`~.os_session.OSSession` with methods specific to Linux nodes.
+The concrete implementation containing all backend logic lives in the framework package.
"""
-import json
-import re
-from collections.abc import Iterable
-from functools import cached_property
-from pathlib import PurePath
-from typing import TypedDict
+from abc import ABC, abstractmethod
-from typing_extensions import NotRequired
-from api.exception import (
- ConfigurationError,
- InternalError,
- RemoteCommandExecutionError,
-)
-from api.testbed_model.port import PortInfo
-from api.utils import expand_range
+class LinuxSession(ABC):
+ """Abstract interface for Linux-specific OS session operations.
-from .cpu import LogicalCore
-from .port import Port
-from .posix_session import PosixSession
-
-
-class LshwConfigurationOutput(TypedDict):
- """The relevant parts of ``lshw``'s ``configuration`` section."""
-
- #:
- driver: str
- #:
- link: str
-
-
-class LshwOutput(TypedDict):
- """A model of the relevant information from ``lshw``'s json output.
-
- Example:
- ::
-
- {
- ...
- "businfo" : "pci@0000:08:00.0",
- "logicalname" : "enp8s0",
- "version" : "00",
- "serial" : "52:54:00:59:e1:ac",
- ...
- "configuration" : {
- ...
- "link" : "yes",
- ...
- },
- ...
+ API consumers should type-hint against this class when they need access
+ to Linux-only capabilities beyond the base :class:`~.os_session.OSSession` contract.
"""
- #:
- businfo: str
- #:
- logicalname: NotRequired[str]
- #:
- serial: NotRequired[str]
- #:
- configuration: LshwConfigurationOutput
-
-
-class LinuxSession(PosixSession):
- """The implementation of non-Posix compliant parts of Linux."""
-
- @staticmethod
- def _get_privileged_command(command: str) -> str:
- command = command.replace(r"'", r"\'")
- return f"sudo -- sh -c '{command}'"
-
- def get_remote_cpus(self) -> list[LogicalCore]:
- """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
- cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
- lcores = []
- for cpu_line in cpu_info.splitlines():
- lcore, core, socket, node = map(int, cpu_line.split(","))
- lcores.append(LogicalCore(lcore, core, socket, node))
- return lcores
-
- def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
- """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
- return dpdk_prefix
-
- def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
-
- Raises:
- ConfigurationError: If the given `hugepage_size` is not supported by the OS.
- """
- self._logger.info("Getting Hugepage information.")
- if (
- f"hugepages-{hugepage_size}kB"
- not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
- ):
- raise ConfigurationError("hugepage size not supported by operating system")
- hugepages_total = self._get_hugepages_total(hugepage_size)
- self._numa_nodes = self._get_numa_nodes()
-
- if force_first_numa or hugepages_total < number_of:
- # when forcing numa, we need to clear existing hugepages regardless
- # of size, so they can be moved to the first numa node
- self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
- else:
- self._logger.info("Hugepages already configured.")
- self._mount_huge_pages()
-
- def _get_hugepages_total(self, hugepage_size: int) -> int:
- hugepages_total = self.send_command(
- f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
- ).stdout
- return int(hugepages_total)
-
- def _get_numa_nodes(self) -> list[int]:
- try:
- numa_count = self.send_command(
- "cat /sys/devices/system/node/online", verify=True
- ).stdout
- numa_range = expand_range(numa_count)
- except RemoteCommandExecutionError:
- # the file doesn't exist, meaning the node doesn't support numa
- numa_range = []
- return numa_range
-
- def _mount_huge_pages(self) -> None:
- self._logger.info("Re-mounting Hugepages.")
- hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
- self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
- result = self.send_command(hugapge_fs_cmd)
- if result.stdout == "":
- remote_mount_path = "/mnt/huge"
- self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
- self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
-
- def _supports_numa(self) -> bool:
- # the system supports numa if self._numa_nodes is non-empty and there are more
- # than one numa node (in the latter case it may actually support numa, but
- # there's no reason to do any numa specific configuration)
- return len(self._numa_nodes) > 1
-
- def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
- self._logger.info("Configuring Hugepages.")
- hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
- if force_first_numa and self._supports_numa():
- # clear non-numa hugepages
- self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
- hugepage_config_path = (
- f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
- f"/hugepages-{size}kB/nr_hugepages"
- )
-
- self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
-
- def get_port_info(self, pci_address: str) -> PortInfo:
- """Overrides :meth:`~.os_session.OSSession.get_port_info`.
-
- Raises:
- ConfigurationError: If the port could not be found.
- """
- bus_info = f"pci@{pci_address}"
- port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
- if port is None:
- raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
-
- logical_name = port.get("logicalname", "")
- mac_address = port.get("serial", "")
-
- configuration = port.get("configuration", {})
- driver = configuration.get("driver", "")
- is_link_up = configuration.get("link", "down") == "up"
-
- return PortInfo(mac_address, logical_name, driver, is_link_up)
-
- def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
-
- The :attr:`~.devbind_script_path` property must be setup in order to call this method.
- """
- ports_pci_addrs = " ".join(port.pci for port in ports)
-
- self.send_command(
- f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
- privileged=True,
- verify=True,
- )
-
- del self._lshw_net_info
-
- def bring_up_link(self, ports: Iterable[Port]) -> None:
- """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
- for port in ports:
- self.send_command(
- f"ip link set dev {port.logical_name} up", privileged=True, verify=True
- )
-
- del self._lshw_net_info
-
- def set_interface_link_up(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
- self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
-
- def delete_interface(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
- self.send_command(f"ip link delete {name}", privileged=True)
-
- @cached_property
- def devbind_script_path(self) -> PurePath:
- """The path to the dpdk-devbind.py script on the node.
-
- Needs to be manually assigned first in order to be used.
-
- Raises:
- InternalError: If accessed before environment setup.
- """
- raise InternalError("Accessed devbind script path before setup.")
-
- def load_vfio(self, pf_port: Port) -> None:
- """Overrides :meth:`~os_session.OSSession,load_vfio`."""
- cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
- device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
- if device and device.group(1) in ["37c8", "0435", "19e2"]:
- self.send_command(
- "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
- privileged=True,
- )
- self.send_command(
- "modprobe -r vfio_virqfd; modprobe -r vfio",
- privileged=True,
- )
- self.send_command(
- "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
- )
- self.send_command(
- "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
- privileged=True,
- )
- else:
- self.send_command("modprobe vfio-pci")
- self.refresh_lshw()
-
- def create_crypto_vfs(self, pf_port: list[Port]) -> None:
- """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
-
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
- """
- for port in pf_port:
- self.delete_crypto_vfs(port)
- for port in pf_port:
- sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
- )
- self.send_command(
- f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
- )
-
- self.refresh_lshw()
-
- def create_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+ @abstractmethod
+ def configure_ipv4_forwarding(self, enable: bool) -> None:
+ """Enable or disable IPv4 forwarding on the node.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ enable: True to enable forwarding, False to disable.
"""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- if curr_num_vfs == 0:
- self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
- self.refresh_lshw()
-
- def delete_crypto_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
- self.send_command(
- f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
- privileged=True,
- )
- self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
-
- def delete_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if curr_num_vfs == 0:
- self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
- else:
- self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
-
- def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- f"readlink {sys_bus_path}/virtfn*",
- privileged=True,
- )
- return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
- return []
-
- def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
- + f"{sys_bus_path}/virtfn*/uevent",
- privileged=True,
- )
- return pci_addrs.stdout.splitlines()
- else:
- return []
-
- @cached_property
- def _lshw_net_info(self) -> list[LshwOutput]:
- output = self.send_command("lshw -quiet -json -C network", verify=True)
- return json.loads(output.stdout)
-
- def refresh_lshw(self) -> None:
- """Force refresh of cached lshw network info."""
- if "_lshw_net_info" in self.__dict__:
- del self.__dict__["_lshw_net_info"]
- _ = self._lshw_net_info
-
- def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
- if attr_value:
- setattr(port, attr_name, attr_value)
- self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
- else:
- self._logger.warning(
- f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
- )
-
- def configure_port_mtu(self, mtu: int, port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
- self.send_command(
- f"ip link set dev {port.logical_name} mtu {mtu}",
- privileged=True,
- verify=True,
- )
-
- def configure_ipv4_forwarding(self, enable: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
- state = 1 if enable else 0
- self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
diff --git a/dts/api/testbed_model/node.py b/dts/api/testbed_model/node.py
index 40dd7f0666..3cab51855d 100644
--- a/dts/api/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -25,7 +25,6 @@
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
-from .linux_session import LinuxSession
from .os_session import OSSession, OSSessionInfo
from .port import Port
@@ -201,16 +200,26 @@ def close(self) -> None:
def create_session(node_config: NodeConfiguration, name: str, logger: DTSLogger) -> OSSession:
"""Factory for OS-aware sessions.
+ Creates a concrete :class:`~.os_session.OSSession` implementation appropriate for the
+ operating system specified in `node_config`. The concrete session classes live in the
+ framework package and are imported lazily to avoid circular dependencies between the
+ API and framework layers.
+
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.
+ Returns:
+ An OS-aware session connected to the node.
+
Raises:
ConfigurationError: If the node's OS is unsupported.
"""
match node_config.os:
case OS.linux:
+ from framework.linux_session import LinuxSession
+
return LinuxSession(node_config, name, logger)
case _:
raise ConfigurationError(f"Unsupported OS {node_config.os}")
diff --git a/dts/api/testbed_model/os_session.py b/dts/api/testbed_model/os_session.py
index b1e0538ac9..618a5bc45c 100644
--- a/dts/api/testbed_model/os_session.py
+++ b/dts/api/testbed_model/os_session.py
@@ -166,6 +166,22 @@ def close(self) -> None:
"""Close the underlying remote session."""
self.remote_session.close()
+ @property
+ @abstractmethod
+ def devbind_script_path(self) -> PurePath:
+ """The path to the dpdk-devbind.py script on the node.
+
+ Must be set up during environment initialization before access.
+
+ Raises:
+ InternalError: If accessed before environment setup.
+ """
+
+ @devbind_script_path.setter
+ @abstractmethod
+ def devbind_script_path(self, value: PurePath) -> None:
+ """Set the devbind script path after environment setup."""
+
@staticmethod
@abstractmethod
def _get_privileged_command(command: str) -> str:
diff --git a/dts/framework/linux_session.py b/dts/framework/linux_session.py
new file mode 100644
index 0000000000..54c6370d53
--- /dev/null
+++ b/dts/framework/linux_session.py
@@ -0,0 +1,375 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2023 University of New Hampshire
+
+"""Linux OS session implementation.
+
+Translates OS-unaware calls into Linux-specific commands and utilities.
+Implements the :class:`~api.linux_session.LinuxSession` contract for Linux distributions,
+building on :class:`~.posix_session.PosixSession` for POSIX-compliant operations.
+"""
+
+import json
+import re
+from collections.abc import Iterable
+from pathlib import PurePath
+from typing import TypedDict
+
+from typing_extensions import NotRequired
+
+from api.exception import (
+ ConfigurationError,
+ InternalError,
+ RemoteCommandExecutionError,
+)
+from api.testbed_model.cpu import LogicalCore
+from api.testbed_model.linux_session import LinuxSession as LinuxSessionBase
+from api.testbed_model.port import Port, PortInfo
+from api.testbed_model.posix_session import PosixSession
+from api.utils import expand_range
+
+
+class LshwConfigurationOutput(TypedDict):
+ """The relevant parts of ``lshw``'s ``configuration`` section."""
+
+ #:
+ driver: str
+ #:
+ link: str
+
+
+class LshwOutput(TypedDict):
+ """A model of the relevant information from ``lshw``'s json output.
+
+ Example:
+ ::
+
+ {
+ ...
+ "businfo" : "pci@0000:08:00.0",
+ "logicalname" : "enp8s0",
+ "version" : "00",
+ "serial" : "52:54:00:59:e1:ac",
+ ...
+ "configuration" : {
+ ...
+ "link" : "yes",
+ ...
+ },
+ ...
+ """
+
+ #:
+ businfo: str
+ #:
+ logicalname: NotRequired[str]
+ #:
+ serial: NotRequired[str]
+ #:
+ configuration: LshwConfigurationOutput
+
+
+class LinuxSession(PosixSession, LinuxSessionBase):
+ """Linux-specific implementation of the OS session interface.
+
+ Inherits POSIX-compliant operations from
+ :class:`~.posix_session.PosixSession` and implements the
+ :class:`~api.linux_session.LinuxSession` contract. All backend logic —
+ ``lshw`` caching, hugepage sysfs interaction, VFIO module loading,
+ SR-IOV management — lives here in the framework.
+ """
+
+ _devbind_script_path: PurePath | None
+
+ def __init__(self, *args, **kwargs) -> None:
+ """Overrides :meth:`~.os_session.OSSession.__init__`."""
+ self._devbind_script_path = None
+ super().__init__(*args, **kwargs)
+
+ @staticmethod
+ def _get_privileged_command(command: str) -> str:
+ command = command.replace(r"'", r"\'")
+ return f"sudo -- sh -c '{command}'"
+
+ def get_remote_cpus(self) -> list[LogicalCore]:
+ """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
+ cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
+ lcores = []
+ for cpu_line in cpu_info.splitlines():
+ lcore, core, socket, node = map(int, cpu_line.split(","))
+ lcores.append(LogicalCore(lcore, core, socket, node))
+ return lcores
+
+ def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
+ """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
+ return dpdk_prefix
+
+ @property
+ def devbind_script_path(self) -> PurePath:
+ """Overrides :attr:`~.os_session.OSSession.devbind_script_path`.
+
+ Raises:
+ InternalError: If accessed before environment setup.
+ """
+ if self._devbind_script_path is None:
+ raise InternalError("Accessed devbind script path before setup.")
+ return self._devbind_script_path
+
+ @devbind_script_path.setter
+ def devbind_script_path(self, value: PurePath) -> None:
+ """Set the devbind script path after environment setup."""
+ self._devbind_script_path = value
+
+ def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
+
+ Raises:
+ ConfigurationError: If the given `hugepage_size` is not supported by the OS.
+ """
+ self._logger.info("Getting Hugepage information.")
+ if (
+ f"hugepages-{hugepage_size}kB"
+ not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
+ ):
+ raise ConfigurationError("hugepage size not supported by operating system")
+ hugepages_total = self._get_hugepages_total(hugepage_size)
+ self._numa_nodes = self._get_numa_nodes()
+
+ if force_first_numa or hugepages_total < number_of:
+ self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
+ else:
+ self._logger.info("Hugepages already configured.")
+ self._mount_huge_pages()
+
+ def _get_hugepages_total(self, hugepage_size: int) -> int:
+ hugepages_total = self.send_command(
+ f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
+ ).stdout
+ return int(hugepages_total)
+
+ def _get_numa_nodes(self) -> list[int]:
+ try:
+ numa_count = self.send_command(
+ "cat /sys/devices/system/node/online", verify=True
+ ).stdout
+ numa_range = expand_range(numa_count)
+ except RemoteCommandExecutionError:
+ numa_range = []
+ return numa_range
+
+ def _mount_huge_pages(self) -> None:
+ self._logger.info("Re-mounting Hugepages.")
+ hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
+ self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
+ result = self.send_command(hugapge_fs_cmd)
+ if result.stdout == "":
+ remote_mount_path = "/mnt/huge"
+ self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
+ self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
+
+ def _supports_numa(self) -> bool:
+ return len(self._numa_nodes) > 1
+
+ def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
+ self._logger.info("Configuring Hugepages.")
+ hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
+ if force_first_numa and self._supports_numa():
+ self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
+ hugepage_config_path = (
+ f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
+ f"/hugepages-{size}kB/nr_hugepages"
+ )
+ self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
+
+ def get_port_info(self, pci_address: str) -> PortInfo:
+ """Overrides :meth:`~.os_session.OSSession.get_port_info`.
+
+ Raises:
+ ConfigurationError: If the port could not be found.
+ """
+ bus_info = f"pci@{pci_address}"
+ port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
+ if port is None:
+ raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
+
+ logical_name = port.get("logicalname", "")
+ mac_address = port.get("serial", "")
+
+ configuration = port.get("configuration", {})
+ driver = configuration.get("driver", "")
+ is_link_up = configuration.get("link", "down") == "up"
+
+ return PortInfo(mac_address, logical_name, driver, is_link_up)
+
+ @property
+ def _lshw_net_info(self) -> list[LshwOutput]:
+ """Cached lshw network info, fetched on first access."""
+ if not hasattr(self, "_lshw_net_cache"):
+ output = self.send_command("lshw -quiet -json -C network", verify=True)
+ self._lshw_net_cache: list[LshwOutput] = json.loads(output.stdout)
+ return self._lshw_net_cache
+
+ def _refresh_lshw(self) -> None:
+ """Force refresh of cached lshw network info.
+
+ Called internally after operations that change port/driver state.
+ """
+ if hasattr(self, "_lshw_net_cache"):
+ del self._lshw_net_cache
+ _ = self._lshw_net_info
+
+ def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
+ if attr_value:
+ setattr(port, attr_name, attr_value)
+ self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
+ else:
+ self._logger.warning(
+ f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
+ )
+
+ def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`."""
+ ports_pci_addrs = " ".join(port.pci for port in ports)
+ self.send_command(
+ f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
+ privileged=True,
+ verify=True,
+ )
+ self._refresh_lshw()
+
+ def bring_up_link(self, ports: Iterable[Port]) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
+ for port in ports:
+ self.send_command(
+ f"ip link set dev {port.logical_name} up", privileged=True, verify=True
+ )
+ self._refresh_lshw()
+
+ def set_interface_link_up(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
+ self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
+
+ def delete_interface(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
+ self.send_command(f"ip link delete {name}", privileged=True)
+
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self._refresh_lshw()
+
+ def create_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ if curr_num_vfs == 0:
+ self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ self._refresh_lshw()
+
+ def delete_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if curr_num_vfs == 0:
+ self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
+ else:
+ self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+
+ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
+ + f"{sys_bus_path}/virtfn*/uevent",
+ privileged=True,
+ )
+ return pci_addrs.stdout.splitlines()
+ else:
+ return []
+
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~.os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+ self._refresh_lshw()
+
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
+ def configure_port_mtu(self, mtu: int, port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
+ self.send_command(
+ f"ip link set dev {port.logical_name} mtu {mtu}",
+ privileged=True,
+ verify=True,
+ )
+
+ def configure_ipv4_forwarding(self, enable: bool) -> None:
+ """Implements :meth:`~api.linux_session.LinuxSession.configure_ipv4_forwarding`."""
+ state = 1 if enable else 0
+ self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 0/7] dts: move test suite imports from framework to API
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
` (6 preceding siblings ...)
2026-04-23 19:04 ` [PATCH v2 7/7] dts: separate Linux session into interface and logic Dean Marx
@ 2026-04-24 17:01 ` Dean Marx
2026-04-24 17:01 ` [PATCH v3 1/7] dts: move exception module " Dean Marx
` (9 more replies)
7 siblings, 10 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-24 17:01 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
This series moves various modules from the framework directory
to the API based on which are being imported by test suites.
These include:
- test_suite
- testbed_model
- exception
- utils
- context
- params
This eliminates all test suite framework imports except for
the remote_session imports in packet_capture, as well
as the settings/config imports in smoke_tests. I believe these
imports, and what to do with them, should be a topic of discussion
in future DTS meetings, as I don't believe they should reside in the
API, even if they are being imported in test suites.
In addition to these changes, I've split the linux_session module
into an interface in api/testbed_model/linux_session and an
implementation in framework/linux_session. This way, users
can still import linux session if necessary, without exposing
the backend implementation.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
v2:
* Integrated rst updates with corresponding commits for build testing
v3:
* Fixed commit ordering to resolve import inconsistency
* Updated Linux Session interface with test suite methods
Dean Marx (7):
dts: move exception module from framework to API
dts: move utils from framework to API
dts: move context from framework to API
dts: move testbed model from framework to API
dts: move test suite module from framework to API
dts: move params directory from framework to API
dts: separate Linux session into interface and logic
...{framework.context.rst => api.context.rst} | 2 +-
...mework.exception.rst => api.exception.rst} | 2 +-
...work.params.eal.rst => api.params.eal.rst} | 2 +-
.../{framework.params.rst => api.params.rst} | 6 +--
....params.types.rst => api.params.types.rst} | 2 +-
...work.test_suite.rst => api.test_suite.rst} | 2 +-
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 ++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
.../{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/framework.linux_session.rst | 6 +++
doc/api/dts/framework.testbed_model.rst | 28 -------------
...mework.testbed_model.traffic_generator.rst | 16 --------
doc/api/dts/index.rst | 13 +++---
dts/api/artifact.py | 6 +--
dts/api/capabilities.py | 14 +++----
dts/{framework => api}/context.py | 14 +++----
dts/api/cryptodev/__init__.py | 6 +--
dts/api/cryptodev/config.py | 6 +--
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 18 ++++----
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 8 ++--
dts/{framework => api}/params/types.py | 14 ++++---
dts/api/test.py | 4 +-
dts/{framework => api}/test_suite.py | 15 ++++---
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 18 ++++----
dts/{framework => api}/testbed_model/cpu.py | 4 +-
dts/api/testbed_model/linux_session.py | 41 +++++++++++++++++++
dts/{framework => api}/testbed_model/node.py | 6 +--
.../testbed_model/os_session.py | 8 ++--
dts/{framework => api}/testbed_model/port.py | 2 +-
.../testbed_model/posix_session.py | 6 +--
.../testbed_model/topology.py | 20 ++++-----
.../traffic_generator/__init__.py | 4 +-
.../capturing_traffic_generator.py | 4 +-
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 14 +++----
.../traffic_generator/traffic_generator.py | 8 ++--
.../testbed_model/traffic_generator/trex.py | 16 ++++----
.../testbed_model/virtual_device.py | 0
dts/api/testpmd/__init__.py | 11 +++--
dts/api/testpmd/config.py | 6 +--
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 2 +-
dts/framework/config/__init__.py | 6 +--
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 12 +++---
.../{testbed_model => }/linux_session.py | 15 ++++---
dts/framework/parser.py | 2 +-
dts/framework/remote_session/blocking_app.py | 8 ++--
dts/framework/remote_session/dpdk.py | 16 ++++----
dts/framework/remote_session/dpdk_shell.py | 6 +--
.../interactive_remote_session.py | 2 +-
.../remote_session/interactive_shell.py | 8 ++--
.../remote_session/remote_session.py | 4 +-
dts/framework/runner.py | 4 +-
dts/framework/test_result.py | 5 +--
dts/framework/test_run.py | 24 +++++------
dts/tests/TestSuite_blocklist.py | 4 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 8 ++--
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 4 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 8 ++--
dts/tests/TestSuite_mac_filter.py | 4 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 12 +++---
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
dts/tests/TestSuite_pmd_rss.py | 6 +--
dts/tests/TestSuite_port_control.py | 2 +-
...stSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 12 +++---
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
.../TestSuite_single_core_forward_perf.py | 4 +-
dts/tests/TestSuite_smoke_tests.py | 6 +--
dts/tests/TestSuite_softnic.py | 6 +--
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 8 ++--
dts/tests/TestSuite_vlan.py | 2 +-
98 files changed, 353 insertions(+), 299 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
create mode 100644 doc/api/dts/framework.linux_session.rst
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/context.py (90%)
rename dts/{framework => api}/exception.py (100%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (89%)
rename dts/{framework => api}/params/types.py (94%)
rename dts/{framework => api}/test_suite.py (98%)
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (98%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
create mode 100644 dts/api/testbed_model/linux_session.py
rename dts/{framework => api}/testbed_model/node.py (98%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (98%)
rename dts/{framework => api}/testbed_model/posix_session.py (99%)
rename dts/{framework => api}/testbed_model/topology.py (96%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (95%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (91%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (96%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
rename dts/{framework => api}/utils.py (99%)
rename dts/framework/{testbed_model => }/linux_session.py (97%)
--
2.52.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v3 1/7] dts: move exception module from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
@ 2026-04-24 17:01 ` Dean Marx
2026-04-24 17:01 ` [PATCH v3 2/7] dts: move utils " Dean Marx
` (8 subsequent siblings)
9 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-24 17:01 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites currently import the exception module
from the framework in order to catch certain errors during
test execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.exception.rst => api.exception.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 2 +-
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/config/__init__.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/context.py | 2 +-
dts/framework/parser.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
.../remote_session/interactive_remote_session.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 4 ++--
dts/framework/remote_session/remote_session.py | 4 ++--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 3 +--
dts/framework/test_run.py | 4 ++--
dts/framework/test_suite.py | 2 +-
dts/framework/testbed_model/capability.py | 2 +-
dts/framework/testbed_model/linux_session.py | 2 +-
dts/framework/testbed_model/node.py | 2 +-
dts/framework/testbed_model/port.py | 2 +-
dts/framework/testbed_model/posix_session.py | 2 +-
dts/framework/testbed_model/topology.py | 2 +-
.../testbed_model/traffic_generator/__init__.py | 2 +-
dts/framework/testbed_model/traffic_generator/scapy.py | 2 +-
dts/framework/utils.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
| 2 +-
dts/tests/TestSuite_rte_flow.py | 10 +++++-----
34 files changed, 40 insertions(+), 41 deletions(-)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename dts/{framework => api}/exception.py (100%)
diff --git a/doc/api/dts/framework.exception.rst b/doc/api/dts/api.exception.rst
similarity index 77%
rename from doc/api/dts/framework.exception.rst
rename to doc/api/dts/api.exception.rst
index efb47dc5ae..8e6bff5ee7 100644
--- a/doc/api/dts/framework.exception.rst
+++ b/doc/api/dts/api.exception.rst
@@ -3,6 +3,6 @@
exception - Exceptions
======================
-.. automodule:: framework.exception
+.. automodule:: api.exception
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index c719297c11..01f630e7cd 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -36,7 +36,7 @@ Modules
framework.logger
framework.parser
framework.utils
- framework.exception
+ api.exception
Indices and tables
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 24a2b05063..7d04c7ab49 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -47,7 +47,7 @@
from paramiko import SFTPClient, SFTPFile
from typing_extensions import Buffer
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
from framework.testbed_model.node import Node, NodeIdentifier, get_node
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index a4fafc3713..c6a220dced 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -22,8 +22,8 @@
ThroughputResults,
VerifyResults,
)
+from api.exception import RemoteCommandExecutionError, SkippedTestException
from framework.context import get_ctx
-from framework.exception import RemoteCommandExecutionError, SkippedTestException
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/framework/exception.py b/dts/api/exception.py
similarity index 100%
rename from dts/framework/exception.py
rename to dts/api/exception.py
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 094a1b7a9d..cabb39a8dd 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,9 +27,9 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.exception import InternalError
from api.test import fail, log_debug
from framework.context import get_ctx
-from framework.exception import InternalError
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index e17babe0ca..9cad9a9495 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.context import get_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..9498d723d5 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
@@ -55,7 +56,6 @@
VLANOffloadFlag,
)
from framework.context import get_ctx
-from framework.exception import InteractiveCommandExecutionError, InternalError
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index d2f0138e4a..566dc7c4a2 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -35,7 +35,7 @@
from pydantic import Field, TypeAdapter, ValidationError, model_validator
from typing_extensions import Self
-from framework.exception import ConfigurationError
+from api.exception import ConfigurationError
from .common import FrozenModel, ValidationContext
from .node import NodeConfiguration
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 76e24d1785..62aaba033a 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -27,7 +27,7 @@
)
from typing_extensions import TYPE_CHECKING, Self
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 8f1021dc96..7e61c85998 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -8,7 +8,7 @@
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 3075c36857..ebf470ad30 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -15,7 +15,7 @@
from typing_extensions import Self
-from framework.exception import InternalError
+from api.exception import InternalError
class ParserFn(TypedDict):
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index c3575cfcaf..d803a9e4bd 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,6 +13,7 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.exception import ConfigurationError, RemoteFileNotFoundError
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -25,7 +26,6 @@
RemoteDPDKTreeLocation,
)
from framework.context import get_ctx
-from framework.exception import ConfigurationError, RemoteFileNotFoundError
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/interactive_remote_session.py b/dts/framework/remote_session/interactive_remote_session.py
index c8156b4345..04f45e0df8 100644
--- a/dts/framework/remote_session/interactive_remote_session.py
+++ b/dts/framework/remote_session/interactive_remote_session.py
@@ -15,8 +15,8 @@
SSHException,
)
+from api.exception import SSHConnectionError
from framework.config.node import NodeConfiguration
-from framework.exception import SSHConnectionError
from framework.logger import DTSLogger
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index a65cbce209..fdd074be3a 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,12 +29,12 @@
from paramiko import Channel, channel
from typing_extensions import Self
-from framework.context import get_ctx
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from framework.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py
index 158325bb7f..f49966070f 100644
--- a/dts/framework/remote_session/remote_session.py
+++ b/dts/framework/remote_session/remote_session.py
@@ -24,13 +24,13 @@
SSHException,
)
-from framework.config.node import NodeConfiguration
-from framework.exception import (
+from api.exception import (
RemoteCommandExecutionError,
SSHConnectionError,
SSHSessionDeadError,
SSHTimeoutError,
)
+from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.settings import SETTINGS
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index 6ea4749ff4..a0d8039a04 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -12,8 +12,8 @@
import sys
import textwrap
+from api.exception import ConfigurationError
from framework.config.common import ValidationContext
-from framework.exception import ConfigurationError
from framework.test_run import TestRun
from framework.testbed_model.node import Node
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 21faa55dc1..3cecb928ca 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -35,12 +35,11 @@
)
from typing_extensions import OrderedDict
+from api.exception import DTSError, ErrorSeverity, InternalError
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
from framework.testbed_model.os_session import OSSessionInfo
-from .exception import DTSError, ErrorSeverity, InternalError
-
class Result(IntEnum):
"""The possible states that a setup, a teardown or a test case may end up in."""
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..bbaf4f1fdf 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,9 +106,9 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
@@ -136,7 +136,7 @@ class TestRun:
If an error occurs, the current stage is aborted, the error is recorded, everything in
the inner stages is marked as blocked and the run continues in the next iteration
of the same stage. The return code is the highest `severity` of all
- :class:`~.framework.exception.DTSError`\s.
+ :class:`~.api.exception.DTSError`\s.
Example:
An error occurs in a test suite setup. The current test suite is aborted,
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 69ce26040a..e06fdd28b9 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -29,11 +29,11 @@
from scapy.packet import Packet
from typing_extensions import Self
+from api.exception import ConfigurationError, InternalError
from framework.config.common import FrozenModel
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
-from .exception import ConfigurationError, InternalError
from .logger import DTSLogger, get_dts_logger
from .utils import to_pascal_case
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 960370fc72..001b65994c 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -64,7 +64,7 @@ def test_scatter_mbuf_2048(self):
from typing_extensions import Self
from api.capabilities import LinkTopology, NicCapability
-from framework.exception import ConfigurationError, InternalError, SkippedTestException
+from api.exception import ConfigurationError, InternalError, SkippedTestException
from framework.logger import get_dts_logger
from framework.testbed_model.node import Node
from framework.testbed_model.port import DriverKind
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index ee943462c2..88b6da1ae6 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -18,7 +18,7 @@
from typing_extensions import NotRequired
-from framework.exception import (
+from api.exception import (
ConfigurationError,
InternalError,
RemoteCommandExecutionError,
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 67a96ef4e5..4f42bf6aeb 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -17,11 +17,11 @@
from pathlib import PurePath
from typing import Literal, TypeAlias
+from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
-from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
diff --git a/dts/framework/testbed_model/port.py b/dts/framework/testbed_model/port.py
index d81bc4cda0..aea3e59c25 100644
--- a/dts/framework/testbed_model/port.py
+++ b/dts/framework/testbed_model/port.py
@@ -12,8 +12,8 @@
from functools import cached_property
from typing import TYPE_CHECKING, Any, Final, Literal, NamedTuple
+from api.exception import InternalError
from framework.config.node import PortConfig
-from framework.exception import InternalError
if TYPE_CHECKING:
from .node import Node
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/framework/testbed_model/posix_session.py
index dec952685a..db2c3c0c40 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -16,7 +16,7 @@
from collections.abc import Iterable
from pathlib import Path, PurePath, PurePosixPath
-from framework.exception import DPDKBuildError, RemoteCommandExecutionError
+from api.exception import DPDKBuildError, RemoteCommandExecutionError
from framework.settings import SETTINGS
from framework.utils import (
MesonArgs,
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 34862c4d2e..805a762c19 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -18,7 +18,7 @@
from typing_extensions import Self
from api.capabilities import LinkTopology
-from framework.exception import ConfigurationError, InternalError
+from api.exception import ConfigurationError, InternalError
from framework.testbed_model.linux_session import LinuxSession
from framework.testbed_model.node import Node, NodeIdentifier
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/framework/testbed_model/traffic_generator/__init__.py
index fca251f534..324b5e88f3 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/framework/testbed_model/traffic_generator/__init__.py
@@ -14,12 +14,12 @@
and a capturing traffic generator is required.
"""
+from api.exception import ConfigurationError
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.exception import ConfigurationError
from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/framework/testbed_model/traffic_generator/scapy.py
index c6e9006205..e983443548 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/framework/testbed_model/traffic_generator/scapy.py
@@ -25,9 +25,9 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet
+from api.exception import InteractiveSSHSessionDeadError, InternalError
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
-from framework.exception import InteractiveSSHSessionDeadError, InternalError
from framework.remote_session.python_shell import PythonShell
from framework.testbed_model.node import Node
from framework.testbed_model.port import Port
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 9917ffbfaa..28e344871a 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -26,7 +26,7 @@
from scapy.layers.inet import IP, TCP, UDP, Ether
from scapy.packet import Packet
-from .exception import InternalError
+from api.exception import InternalError
REGEX_FOR_PCI_ADDRESS: str = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}"
_REGEX_FOR_COLON_OR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}[:-]){5}[\da-fA-F]{2}"
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index af0a5680ab..f36b48a153 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -29,9 +29,9 @@
from api.cryptodev.types import (
CryptodevResults,
)
+from api.exception import SkippedTestException
from api.test import verify
from framework.context import get_ctx
-from framework.exception import SkippedTestException
from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index 5ac85bee7d..b62efa2b42 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -35,11 +35,11 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import TestSuite, func_test
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index a7e24b37d5..b44822d31c 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -23,10 +23,10 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
from api.testpmd import TestPmd
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import TestSuite, func_test
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index f6adf262c3..1e5a6860be 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -20,6 +20,7 @@
requires_link_topology,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
from api.testpmd import TestPmd
@@ -29,7 +30,6 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import BaseConfig, TestSuite, func_test
from framework.utils import StrEnum
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 6255e4c36d..7e50a075ac 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -21,15 +21,15 @@
from scapy.packet import Packet, Raw
from api.capabilities import NicCapability, requires_nic_capability
-from api.packet import send_packet_and_capture
-from api.test import fail, log, verify
-from api.testpmd import TestPmd
-from api.testpmd.types import FlowRule
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
SkippedTestException,
TestCaseVerifyError,
)
+from api.packet import send_packet_and_capture
+from api.test import fail, log, verify
+from api.testpmd import TestPmd
+from api.testpmd.types import FlowRule
from framework.test_suite import TestSuite, func_test
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 2/7] dts: move utils from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-24 17:01 ` [PATCH v3 1/7] dts: move exception module " Dean Marx
@ 2026-04-24 17:01 ` Dean Marx
2026-04-24 17:01 ` [PATCH v3 3/7] dts: move context " Dean Marx
` (7 subsequent siblings)
9 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-24 17:01 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The utils module is used to generate a set of random
packets in certain test suites. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/config.py | 2 +-
dts/api/packet.py | 2 +-
dts/api/testpmd/config.py | 2 +-
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 0
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/test_suite.py | 2 +-
dts/framework/testbed_model/cpu.py | 2 +-
dts/framework/testbed_model/linux_session.py | 2 +-
dts/framework/testbed_model/os_session.py | 2 +-
dts/framework/testbed_model/posix_session.py | 4 ++--
.../traffic_generator/capturing_traffic_generator.py | 2 +-
.../testbed_model/traffic_generator/traffic_generator.py | 2 +-
dts/framework/testbed_model/traffic_generator/trex.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
| 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
22 files changed, 22 insertions(+), 22 deletions(-)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
rename dts/{framework => api}/utils.py (100%)
diff --git a/doc/api/dts/framework.utils.rst b/doc/api/dts/api.utils.rst
similarity index 80%
rename from doc/api/dts/framework.utils.rst
rename to doc/api/dts/api.utils.rst
index cc06d4c3c3..0b4baff1b5 100644
--- a/doc/api/dts/framework.utils.rst
+++ b/doc/api/dts/api.utils.rst
@@ -3,6 +3,6 @@
utils - Various Utilities
=========================
-.. automodule:: framework.utils
+.. automodule:: api.utils
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 01f630e7cd..783270f6e9 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -35,7 +35,7 @@ Modules
framework.context
framework.logger
framework.parser
- framework.utils
+ api.utils
api.exception
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index 69ff7aa59a..a88e70d45c 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.utils import StrEnum
from framework.params import Params, Switch
from framework.params.eal import EalParams
-from framework.utils import StrEnum
Silent = Literal[""]
diff --git a/dts/api/packet.py b/dts/api/packet.py
index cabb39a8dd..b7a9bb28bf 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -29,6 +29,7 @@
from api.exception import InternalError
from api.test import fail, log_debug
+from api.utils import get_packet_summaries
from framework.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -36,7 +37,6 @@
from framework.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
-from framework.utils import get_packet_summaries
def send_packet_and_capture(
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index e71a3e1ef0..8b688834ee 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,6 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
+from api.utils import StrEnum
from framework.params import (
Params,
Switch,
@@ -24,7 +25,6 @@
str_from_flag_value,
)
from framework.params.eal import EalParams
-from framework.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..5c847b4bd6 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -15,8 +15,8 @@
from typing_extensions import Self
+from api.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
from framework.parser import ParserFn, TextParser
-from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
RxTxLiteralSwitch = Literal["rx", "tx"]
diff --git a/dts/framework/utils.py b/dts/api/utils.py
similarity index 100%
rename from dts/framework/utils.py
rename to dts/api/utils.py
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 792290f11f..28f23389a7 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -14,7 +14,7 @@
from pydantic import Field, model_validator
from typing_extensions import Self
-from framework.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
+from api.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
from .common import FrozenModel
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 62aaba033a..977067f42a 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -28,7 +28,7 @@
from typing_extensions import TYPE_CHECKING, Self
from api.exception import InternalError
-from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
+from api.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index d803a9e4bd..69b47b823b 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -14,6 +14,7 @@
from typing import ClassVar, Final
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -33,7 +34,6 @@
from framework.testbed_model.node import Node
from framework.testbed_model.os_session import OSSession
from framework.testbed_model.virtual_device import VirtualDevice
-from framework.utils import MesonArgs, TarCompressionFormat
@dataclass(slots=True, frozen=True)
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index e06fdd28b9..426c98fdf6 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -30,12 +30,12 @@
from typing_extensions import Self
from api.exception import ConfigurationError, InternalError
+from api.utils import to_pascal_case
from framework.config.common import FrozenModel
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
from .logger import DTSLogger, get_dts_logger
-from .utils import to_pascal_case
if TYPE_CHECKING:
from framework.context import Context
diff --git a/dts/framework/testbed_model/cpu.py b/dts/framework/testbed_model/cpu.py
index 6e2ecca080..52ef196f84 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/framework/testbed_model/cpu.py
@@ -24,7 +24,7 @@
from dataclasses import dataclass
from enum import auto, unique
-from framework.utils import StrEnum, expand_range
+from api.utils import StrEnum, expand_range
@unique
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 88b6da1ae6..69b0923744 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -23,8 +23,8 @@
InternalError,
RemoteCommandExecutionError,
)
+from api.utils import expand_range
from framework.testbed_model.port import PortInfo
-from framework.utils import expand_range
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index 2c267afed1..7bb339fab2 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -29,12 +29,12 @@
from enum import Flag, auto
from pathlib import Path, PurePath, PurePosixPath
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.remote_session.interactive_remote_session import InteractiveRemoteSession
from framework.remote_session.remote_session import CommandResult, RemoteSession
from framework.settings import SETTINGS
-from framework.utils import MesonArgs, TarCompressionFormat
from .cpu import Architecture, LogicalCore
from .port import Port, PortInfo
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/framework/testbed_model/posix_session.py
index db2c3c0c40..61c634dad1 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -17,14 +17,14 @@
from pathlib import Path, PurePath, PurePosixPath
from api.exception import DPDKBuildError, RemoteCommandExecutionError
-from framework.settings import SETTINGS
-from framework.utils import (
+from api.utils import (
MesonArgs,
TarCompressionFormat,
convert_to_list_of_string,
create_tarball,
extract_tarball,
)
+from framework.settings import SETTINGS
from .cpu import Architecture
from .os_session import FilePermissions, OSSession, OSSessionInfo
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
index 7655751d7e..2804d64990 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
+from api.utils import get_packet_summaries
from framework.testbed_model.port import Port
-from framework.utils import get_packet_summaries
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/framework/testbed_model/traffic_generator/traffic_generator.py
index cdda5a7c08..fedce77fdf 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/framework/testbed_model/traffic_generator/traffic_generator.py
@@ -25,7 +25,7 @@ class TrafficGenerator(ABC):
Exposes the common public methods of all traffic generators and defines private methods
that must implement the traffic generation logic in subclasses. This class also extends from
- :class:`framework.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
+ :class:`api.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
from multiple classes to fulfil the traffic generating functionality without breaking
single inheritance.
"""
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/framework/testbed_model/traffic_generator/trex.py
index 22cd20dea9..2064703fcc 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/framework/testbed_model/traffic_generator/trex.py
@@ -11,6 +11,7 @@
from scapy.packet import Packet
+from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
@@ -23,7 +24,6 @@
PerformanceTrafficGenerator,
PerformanceTrafficStats,
)
-from framework.utils import StrEnum
@dataclass(slots=True)
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 596b892730..1e99b82b8c 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -20,10 +20,10 @@
)
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
+from api.utils import generate_random_packets
from framework.context import filter_cores
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 1e5a6860be..4df273e3e1 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -30,8 +30,8 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
+from api.utils import StrEnum
from framework.test_suite import BaseConfig, TestSuite, func_test
-from framework.utils import StrEnum
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 271ad4301c..fce83604a6 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -20,11 +20,11 @@
)
from api.test import verify
from api.testpmd import TestPmd
+from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.linux_session import LinuxSession
-from framework.utils import REGEX_FOR_PCI_ADDRESS
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index fa91f7ee2f..c57a12c932 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -20,9 +20,9 @@
)
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
+from api.utils import generate_random_packets
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.virtual_device import VirtualDevice
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 3/7] dts: move context from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-24 17:01 ` [PATCH v3 1/7] dts: move exception module " Dean Marx
2026-04-24 17:01 ` [PATCH v3 2/7] dts: move utils " Dean Marx
@ 2026-04-24 17:01 ` Dean Marx
2026-04-24 17:01 ` [PATCH v3 4/7] dts: move testbed model " Dean Marx
` (6 subsequent siblings)
9 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-24 17:01 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
A couple test suites import and get the run context
during execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.context.rst => api.context.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/{framework => api}/context.py | 0
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 4 ++--
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/framework/test_run.py | 2 +-
dts/framework/test_suite.py | 4 ++--
dts/framework/testbed_model/node.py | 2 +-
dts/framework/testbed_model/topology.py | 14 +++++++-------
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
18 files changed, 25 insertions(+), 25 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename dts/{framework => api}/context.py (100%)
diff --git a/doc/api/dts/framework.context.rst b/doc/api/dts/api.context.rst
similarity index 80%
rename from doc/api/dts/framework.context.rst
rename to doc/api/dts/api.context.rst
index 925c160360..65b4ab9a47 100644
--- a/doc/api/dts/framework.context.rst
+++ b/doc/api/dts/api.context.rst
@@ -3,6 +3,6 @@
context - DTS execution context
===============================
-.. automodule:: framework.context
+.. automodule:: api.context
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 783270f6e9..98269d6e80 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -32,7 +32,7 @@ Modules
framework.test_suite
framework.test_result
framework.settings
- framework.context
+ api.context
framework.logger
framework.parser
api.utils
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 7d04c7ab49..f3dd07de56 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -86,7 +86,7 @@ def make_file_path(
path /= custom_path
else:
- from framework.context import get_ctx
+ from api.context import get_ctx
try:
ctx = get_ctx()
diff --git a/dts/framework/context.py b/dts/api/context.py
similarity index 100%
rename from dts/framework/context.py
rename to dts/api/context.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index c6a220dced..67dcb02130 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -23,7 +23,7 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index b7a9bb28bf..59f26da833 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -30,7 +30,7 @@
from api.exception import InternalError
from api.test import fail, log_debug
from api.utils import get_packet_summaries
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
@@ -82,7 +82,7 @@ def send_packets_and_capture(
Returns:
A list of received packets.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index 9cad9a9495..a1f2326075 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -11,7 +11,7 @@
from api.artifact import Artifact
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9498d723d5..a528663c21 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -55,7 +55,7 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index c3b02dcc62..07db6dfeb0 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -30,7 +30,7 @@
from typing_extensions import Self
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 69b47b823b..9f7cd60dfe 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -26,7 +26,7 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 269c2cada4..a8f169787c 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -10,7 +10,7 @@
from abc import ABC, abstractmethod
from pathlib import PurePath
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index fdd074be3a..d138727c85 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -34,7 +34,7 @@
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index bbaf4f1fdf..605a916ebb 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -108,7 +108,7 @@
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.config.test_run import TestRunConfiguration
-from framework.context import Context, init_ctx
+from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 426c98fdf6..6066f7a77a 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -38,7 +38,7 @@
from .logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
- from framework.context import Context
+ from api.context import Context
class BaseConfig(FrozenModel):
@@ -92,7 +92,7 @@ def __init__(self, config: BaseConfig) -> None:
Args:
config: The test suite configuration.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self.config = config
self._ctx = get_ctx()
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 4f42bf6aeb..40dd7f0666 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -242,7 +242,7 @@ def get_node(node_identifier: NodeIdentifier) -> Node | None:
if node_identifier == "local":
return None
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
if node_identifier == "sut":
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 805a762c19..5b6ff2add5 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -96,7 +96,7 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
Raises:
InternalError: If the given `node_identifier` is invalid.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
match node_identifier:
@@ -180,7 +180,7 @@ def instantiate_crypto_ports(self) -> None:
Raises:
InternalError: If crypto virtual functions could not be created on a port.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
for port in ctx.sut_node.cryptodevs:
@@ -206,7 +206,7 @@ def instantiate_vf_ports(self) -> None:
Raises:
InternalError: If virtual function creation fails.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -235,7 +235,7 @@ def instantiate_vf_ports(self) -> None:
def delete_vf_ports(self) -> None:
"""Delete virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -246,7 +246,7 @@ def delete_vf_ports(self) -> None:
def delete_crypto_vf_ports(self) -> None:
"""Delete crypto virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -259,7 +259,7 @@ def bind_cryptodevs(self, driver: DriverKind):
Args:
driver: The driver to bind the crypto functions
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
@@ -318,7 +318,7 @@ def _prepare_devbind_script(self) -> None:
Raises:
InternalError: If dpdk-devbind.py could not be found.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
local_script_path = Path("..", "usertools", "dpdk-devbind.py").resolve()
valid_script_path = local_script_path.exists()
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index f36b48a153..39784cbcac 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -31,7 +31,7 @@
)
from api.exception import SkippedTestException
from api.test import verify
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 1e99b82b8c..c018efb1f7 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -21,7 +21,7 @@
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.context import filter_cores
+from api.context import filter_cores
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 4/7] dts: move testbed model from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
` (2 preceding siblings ...)
2026-04-24 17:01 ` [PATCH v3 3/7] dts: move context " Dean Marx
@ 2026-04-24 17:01 ` Dean Marx
2026-04-24 17:01 ` [PATCH v3 5/7] dts: move test suite module " Dean Marx
` (5 subsequent siblings)
9 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-24 17:01 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites import modules from testbed model
in the framework. Move this directory to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 +++++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
doc/api/dts/framework.testbed_model.rst | 28 -------------------
...mework.testbed_model.traffic_generator.rst | 16 -----------
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/capabilities.py | 8 +++---
dts/api/context.py | 10 +++----
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 12 ++++----
dts/api/test.py | 2 +-
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 10 +++----
dts/{framework => api}/testbed_model/cpu.py | 2 +-
.../testbed_model/linux_session.py | 2 +-
dts/{framework => api}/testbed_model/node.py | 0
.../testbed_model/os_session.py | 6 ++--
dts/{framework => api}/testbed_model/port.py | 0
.../testbed_model/posix_session.py | 0
.../testbed_model/topology.py | 4 +--
.../traffic_generator/__init__.py | 2 +-
.../capturing_traffic_generator.py | 2 +-
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 12 ++++----
.../traffic_generator/traffic_generator.py | 6 ++--
.../testbed_model/traffic_generator/trex.py | 14 +++++-----
.../testbed_model/virtual_device.py | 0
dts/api/testpmd/__init__.py | 2 +-
dts/framework/params/eal.py | 6 ++--
dts/framework/params/types.py | 6 ++--
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 10 +++----
dts/framework/remote_session/dpdk_shell.py | 2 +-
.../remote_session/interactive_shell.py | 4 +--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 2 +-
dts/framework/test_run.py | 18 ++++++------
dts/framework/test_suite.py | 4 +--
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 4 +--
dts/tests/TestSuite_l2fwd.py | 4 +--
dts/tests/TestSuite_packet_capture.py | 8 +++---
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 6 ++--
57 files changed, 148 insertions(+), 148 deletions(-)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (99%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
rename dts/{framework => api}/testbed_model/linux_session.py (99%)
rename dts/{framework => api}/testbed_model/node.py (100%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (100%)
rename dts/{framework => api}/testbed_model/posix_session.py (100%)
rename dts/{framework => api}/testbed_model/topology.py (99%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (97%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (99%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (94%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (97%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
diff --git a/doc/api/dts/framework.testbed_model.capability.rst b/doc/api/dts/api.testbed_model.capability.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.capability.rst
rename to doc/api/dts/api.testbed_model.capability.rst
index fab91cad83..88e396dddb 100644
--- a/doc/api/dts/framework.testbed_model.capability.rst
+++ b/doc/api/dts/api.testbed_model.capability.rst
@@ -3,6 +3,6 @@
capability - Testbed Capabilities
=================================
-.. automodule:: framework.testbed_model.capability
+.. automodule:: api.testbed_model.capability
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.cpu.rst b/doc/api/dts/api.testbed_model.cpu.rst
similarity index 78%
rename from doc/api/dts/framework.testbed_model.cpu.rst
rename to doc/api/dts/api.testbed_model.cpu.rst
index 997f2a9795..dbbb29480a 100644
--- a/doc/api/dts/framework.testbed_model.cpu.rst
+++ b/doc/api/dts/api.testbed_model.cpu.rst
@@ -3,6 +3,6 @@
cpu - CPU Representation and Utilities
======================================
-.. automodule:: framework.testbed_model.cpu
+.. automodule:: api.testbed_model.cpu
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.linux_session.rst b/doc/api/dts/api.testbed_model.linux_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.linux_session.rst
rename to doc/api/dts/api.testbed_model.linux_session.rst
index 7567816199..cfe79d8bca 100644
--- a/doc/api/dts/framework.testbed_model.linux_session.rst
+++ b/doc/api/dts/api.testbed_model.linux_session.rst
@@ -3,6 +3,6 @@
linux\_session - Linux Remote Session
=====================================
-.. automodule:: framework.testbed_model.linux_session
+.. automodule:: api.testbed_model.linux_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.node.rst b/doc/api/dts/api.testbed_model.node.rst
similarity index 71%
rename from doc/api/dts/framework.testbed_model.node.rst
rename to doc/api/dts/api.testbed_model.node.rst
index 23c6c46a00..15f522e5f7 100644
--- a/doc/api/dts/framework.testbed_model.node.rst
+++ b/doc/api/dts/api.testbed_model.node.rst
@@ -3,6 +3,6 @@
node - Base Node
================
-.. automodule:: framework.testbed_model.node
+.. automodule:: api.testbed_model.node
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.os_session.rst b/doc/api/dts/api.testbed_model.os_session.rst
similarity index 76%
rename from doc/api/dts/framework.testbed_model.os_session.rst
rename to doc/api/dts/api.testbed_model.os_session.rst
index ecfb352311..e7e3f9894f 100644
--- a/doc/api/dts/framework.testbed_model.os_session.rst
+++ b/doc/api/dts/api.testbed_model.os_session.rst
@@ -3,6 +3,6 @@
os\_session - OS-aware Remote Session ABC
=========================================
-.. automodule:: framework.testbed_model.os_session
+.. automodule:: api.testbed_model.os_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.port.rst b/doc/api/dts/api.testbed_model.port.rst
similarity index 77%
rename from doc/api/dts/framework.testbed_model.port.rst
rename to doc/api/dts/api.testbed_model.port.rst
index fdb7ca8a1d..d64501aef0 100644
--- a/doc/api/dts/framework.testbed_model.port.rst
+++ b/doc/api/dts/api.testbed_model.port.rst
@@ -3,7 +3,7 @@
port - NIC Port Representation
==============================
-.. automodule:: framework.testbed_model.port
+.. automodule:: api.testbed_model.port
:members:
:show-inheritance:
:noindex:
diff --git a/doc/api/dts/framework.testbed_model.posix_session.rst b/doc/api/dts/api.testbed_model.posix_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.posix_session.rst
rename to doc/api/dts/api.testbed_model.posix_session.rst
index e65585fd85..9f0e9ff18d 100644
--- a/doc/api/dts/framework.testbed_model.posix_session.rst
+++ b/doc/api/dts/api.testbed_model.posix_session.rst
@@ -3,6 +3,6 @@
posix\_session - Posix Remote Session
=====================================
-.. automodule:: framework.testbed_model.posix_session
+.. automodule:: api.testbed_model.posix_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.rst b/doc/api/dts/api.testbed_model.rst
new file mode 100644
index 0000000000..5e2e5189b2
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.rst
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+testbed\_model - Testbed Modelling Package
+==========================================
+
+.. automodule:: api.testbed_model
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 2
+
+ api.testbed_model.traffic_generator
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.os_session
+ api.testbed_model.linux_session
+ api.testbed_model.posix_session
+ api.testbed_model.node
+ api.testbed_model.capability
+ api.testbed_model.cpu
+ api.testbed_model.port
+ api.testbed_model.topology
+ api.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.topology.rst b/doc/api/dts/api.testbed_model.topology.rst
similarity index 73%
rename from doc/api/dts/framework.testbed_model.topology.rst
rename to doc/api/dts/api.testbed_model.topology.rst
index 496f2a895f..bb63fe38dc 100644
--- a/doc/api/dts/framework.testbed_model.topology.rst
+++ b/doc/api/dts/api.testbed_model.topology.rst
@@ -3,6 +3,6 @@
topology - Testbed Topology
===========================
-.. automodule:: framework.testbed_model.topology
+.. automodule:: api.testbed_model.topology
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
similarity index 68%
rename from doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
index 29fa834042..cfe03201a7 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
@@ -3,6 +3,6 @@
capturing\_traffic\_generator - Base Capturing TG ABC
=====================================================
-.. automodule:: framework.testbed_model.traffic_generator.capturing_traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.capturing_traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.rst
new file mode 100644
index 0000000000..311bdcf6b9
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.traffic_generator.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+traffic\_generator Subpackage
+=============================
+
+.. automodule:: api.testbed_model.traffic_generator
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.traffic_generator.traffic_generator
+ api.testbed_model.traffic_generator.capturing_traffic_generator
+ api.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
similarity index 70%
rename from doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
index df78ac9514..949bb66632 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
@@ -3,6 +3,6 @@
scapy - Capturing Traffic Generator
===================================
-.. automodule:: framework.testbed_model.traffic_generator.scapy
+.. automodule:: api.testbed_model.traffic_generator.scapy
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
similarity index 65%
rename from doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
index bfec728dee..1045e534b5 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
@@ -3,6 +3,6 @@
traffic\_generator - Base TG ABC
================================
-.. automodule:: framework.testbed_model.traffic_generator.traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.virtual_device.rst b/doc/api/dts/api.testbed_model.virtual_device.rst
similarity index 72%
rename from doc/api/dts/framework.testbed_model.virtual_device.rst
rename to doc/api/dts/api.testbed_model.virtual_device.rst
index a6b0420e75..97adc895f6 100644
--- a/doc/api/dts/framework.testbed_model.virtual_device.rst
+++ b/doc/api/dts/api.testbed_model.virtual_device.rst
@@ -3,6 +3,6 @@
virtual\_device - Virtual Devices
=================================
-.. automodule:: framework.testbed_model.virtual_device
+.. automodule:: api.testbed_model.virtual_device
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.rst b/doc/api/dts/framework.testbed_model.rst
deleted file mode 100644
index f283178f6a..0000000000
--- a/doc/api/dts/framework.testbed_model.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-testbed\_model - Testbed Modelling Package
-==========================================
-
-.. automodule:: framework.testbed_model
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 2
-
- framework.testbed_model.traffic_generator
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.os_session
- framework.testbed_model.linux_session
- framework.testbed_model.posix_session
- framework.testbed_model.node
- framework.testbed_model.capability
- framework.testbed_model.cpu
- framework.testbed_model.port
- framework.testbed_model.topology
- framework.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.rst b/doc/api/dts/framework.testbed_model.traffic_generator.rst
deleted file mode 100644
index 24c250ee3a..0000000000
--- a/doc/api/dts/framework.testbed_model.traffic_generator.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-traffic\_generator Subpackage
-=============================
-
-.. automodule:: framework.testbed_model.traffic_generator
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.traffic_generator.traffic_generator
- framework.testbed_model.traffic_generator.capturing_traffic_generator
- framework.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 98269d6e80..f47e4af3f2 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -16,7 +16,7 @@ Packages
tests
api
- framework.testbed_model
+ api.testbed_model
framework.remote_session
framework.params
framework.config
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index f3dd07de56..74a8ac667f 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -48,9 +48,9 @@
from typing_extensions import Buffer
from api.exception import InternalError
+from api.testbed_model.node import Node, NodeIdentifier, get_node
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node, NodeIdentifier, get_node
TextMode: TypeAlias = (
Literal["r", "r+", "w", "w+", "a", "a+", "x", "x+"]
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 09bc538523..04fc20738b 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -23,7 +23,7 @@
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires_link_topology
+ from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -34,7 +34,7 @@ def hello_world_single_core(self):
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires_nic_capability
+ from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -235,7 +235,7 @@ def requires_link_topology(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import TopologyCapability
+ from api.testbed_model.capability import TopologyCapability
def add_required_topology(
test_case_or_suite: type["TestProtocol"],
@@ -258,7 +258,7 @@ def requires_nic_capability(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import DecoratedNicCapability
+ from api.testbed_model.capability import DecoratedNicCapability
def add_required_capability(
test_case_or_suite: type["TestProtocol"],
diff --git a/dts/api/context.py b/dts/api/context.py
index 7e61c85998..13a2ad6c39 100644
--- a/dts/api/context.py
+++ b/dts/api/context.py
@@ -9,17 +9,17 @@
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
from api.exception import InternalError
+from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
+from api.testbed_model.node import Node
+from api.testbed_model.topology import Topology
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
-from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
+ from api.testbed_model.capability import TestProtocol
+ from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.test_suite import TestCase, TestSuite
- from framework.testbed_model.capability import TestProtocol
- from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
P = ParamSpec("P")
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 67dcb02130..1ba8e0d977 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -14,6 +14,7 @@
from typing_extensions import Unpack
+from api.context import get_ctx
from api.cryptodev.config import CryptoPmdParams, TestType
from api.cryptodev.types import (
CryptodevResults,
@@ -23,7 +24,6 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 59f26da833..bf90961c26 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,16 +27,16 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.context import get_ctx
from api.exception import InternalError
from api.test import fail, log_debug
-from api.utils import get_packet_summaries
-from api.context import get_ctx
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
+from api.utils import get_packet_summaries
def send_packet_and_capture(
@@ -83,7 +83,7 @@ def send_packets_and_capture(
A list of received packets.
"""
from api.context import get_ctx
- from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+ from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
@@ -340,7 +340,7 @@ def assess_performance_by_packet(
Returns:
Performance statistics of the generated test.
"""
- from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+ from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficGenerator,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index a1f2326075..03846639ad 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
-from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.context import get_ctx
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/framework/testbed_model/__init__.py b/dts/api/testbed_model/__init__.py
similarity index 100%
rename from dts/framework/testbed_model/__init__.py
rename to dts/api/testbed_model/__init__.py
diff --git a/dts/framework/testbed_model/capability.py b/dts/api/testbed_model/capability.py
similarity index 99%
rename from dts/framework/testbed_model/capability.py
rename to dts/api/testbed_model/capability.py
index 001b65994c..4e4e976be5 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -26,7 +26,7 @@
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires
+ from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -37,7 +37,7 @@ def hello_world_single_core(self):
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires
+ from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -65,10 +65,10 @@ def test_scatter_mbuf_2048(self):
from api.capabilities import LinkTopology, NicCapability
from api.exception import ConfigurationError, InternalError, SkippedTestException
+from api.testbed_model.node import Node
+from api.testbed_model.port import DriverKind
+from api.testbed_model.topology import Topology
from framework.logger import get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import DriverKind
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
from api.testpmd import TestPmd
diff --git a/dts/framework/testbed_model/cpu.py b/dts/api/testbed_model/cpu.py
similarity index 99%
rename from dts/framework/testbed_model/cpu.py
rename to dts/api/testbed_model/cpu.py
index 52ef196f84..ee754f5844 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/api/testbed_model/cpu.py
@@ -29,7 +29,7 @@
@unique
class Architecture(StrEnum):
- r"""The supported architectures of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported architectures of :class:`~api.testbed_model.node.Node`\s."""
#:
i686 = auto()
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
similarity index 99%
rename from dts/framework/testbed_model/linux_session.py
rename to dts/api/testbed_model/linux_session.py
index 69b0923744..7307b2abe2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -23,8 +23,8 @@
InternalError,
RemoteCommandExecutionError,
)
+from api.testbed_model.port import PortInfo
from api.utils import expand_range
-from framework.testbed_model.port import PortInfo
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/node.py b/dts/api/testbed_model/node.py
similarity index 100%
rename from dts/framework/testbed_model/node.py
rename to dts/api/testbed_model/node.py
diff --git a/dts/framework/testbed_model/os_session.py b/dts/api/testbed_model/os_session.py
similarity index 99%
rename from dts/framework/testbed_model/os_session.py
rename to dts/api/testbed_model/os_session.py
index 7bb339fab2..b1e0538ac9 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/api/testbed_model/os_session.py
@@ -73,11 +73,11 @@ class OSSessionInfo:
Attributes:
os_name: The name of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
os_version: The version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
kernel_version: The kernel version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
"""
os_name: str
diff --git a/dts/framework/testbed_model/port.py b/dts/api/testbed_model/port.py
similarity index 100%
rename from dts/framework/testbed_model/port.py
rename to dts/api/testbed_model/port.py
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/api/testbed_model/posix_session.py
similarity index 100%
rename from dts/framework/testbed_model/posix_session.py
rename to dts/api/testbed_model/posix_session.py
diff --git a/dts/framework/testbed_model/topology.py b/dts/api/testbed_model/topology.py
similarity index 99%
rename from dts/framework/testbed_model/topology.py
rename to dts/api/testbed_model/topology.py
index 5b6ff2add5..11593d64d5 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/api/testbed_model/topology.py
@@ -19,8 +19,8 @@
from api.capabilities import LinkTopology
from api.exception import ConfigurationError, InternalError
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.node import Node, NodeIdentifier
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.node import Node, NodeIdentifier
from .port import DriverKind, Port, PortConfig
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/api/testbed_model/traffic_generator/__init__.py
similarity index 97%
rename from dts/framework/testbed_model/traffic_generator/__init__.py
rename to dts/api/testbed_model/traffic_generator/__init__.py
index 324b5e88f3..11fa25448a 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/api/testbed_model/traffic_generator/__init__.py
@@ -15,12 +15,12 @@
"""
from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
similarity index 99%
rename from dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
index 2804d64990..db274e5e82 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
+from api.testbed_model.port import Port
from api.utils import get_packet_summaries
-from framework.testbed_model.port import Port
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py b/dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
similarity index 100%
rename from dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/api/testbed_model/traffic_generator/scapy.py
similarity index 98%
rename from dts/framework/testbed_model/traffic_generator/scapy.py
rename to dts/api/testbed_model/traffic_generator/scapy.py
index e983443548..215c57f93d 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/api/testbed_model/traffic_generator/scapy.py
@@ -26,15 +26,15 @@
from scapy.packet import Packet
from api.exception import InteractiveSSHSessionDeadError, InternalError
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
from .capturing_traffic_generator import CapturingTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/api/testbed_model/traffic_generator/traffic_generator.py
similarity index 94%
rename from dts/framework/testbed_model/traffic_generator/traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/traffic_generator.py
index fedce77fdf..5fd68e5144 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/traffic_generator.py
@@ -13,11 +13,11 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
from framework.config.test_run import TrafficGeneratorConfig
from framework.logger import DTSLogger, get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
class TrafficGenerator(ABC):
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/api/testbed_model/traffic_generator/trex.py
similarity index 97%
rename from dts/framework/testbed_model/traffic_generator/trex.py
rename to dts/api/testbed_model/traffic_generator/trex.py
index 2064703fcc..d97ed934c9 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/api/testbed_model/traffic_generator/trex.py
@@ -11,19 +11,19 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node, create_session
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
+ PerformanceTrafficGenerator,
+ PerformanceTrafficStats,
+)
from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node, create_session
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
- PerformanceTrafficGenerator,
- PerformanceTrafficStats,
-)
@dataclass(slots=True)
diff --git a/dts/framework/testbed_model/virtual_device.py b/dts/api/testbed_model/virtual_device.py
similarity index 100%
rename from dts/framework/testbed_model/virtual_device.py
rename to dts/api/testbed_model/virtual_device.py
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index a528663c21..9f47a15433 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.context import get_ctx
from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
@@ -55,7 +56,6 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/params/eal.py b/dts/framework/params/eal.py
index e84a20f02f..86bfd3fcc6 100644
--- a/dts/framework/params/eal.py
+++ b/dts/framework/params/eal.py
@@ -6,12 +6,12 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.virtual_device import VirtualDevice
from framework.params import Params, Switch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.virtual_device import VirtualDevice
if TYPE_CHECKING:
- from framework.testbed_model.port import Port
+ from api.testbed_model.port import Port
def _port_to_pci(port: "Port") -> str:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 3c7650474c..f2fa69f8b8 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -32,6 +32,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.port import Port
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -54,9 +57,6 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TxUDPPortPair,
)
from framework.params import Switch, YesNoSwitch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.port import Port
-from framework.testbed_model.virtual_device import VirtualDevice
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 07db6dfeb0..84db3974b1 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,11 +31,11 @@
from typing_extensions import Self
from api.context import get_ctx
+from api.testbed_model.node import Node
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
-from framework.testbed_model.node import Node
P = TypeVar("P", bound=Params)
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 9f7cd60dfe..713a564d25 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,7 +13,12 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
+from api.testbed_model.node import Node
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
@@ -26,14 +31,9 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
-from framework.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
-from framework.testbed_model.node import Node
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.virtual_device import VirtualDevice
@dataclass(slots=True, frozen=True)
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index a8f169787c..b807f9bdae 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,12 +11,12 @@
from pathlib import PurePath
from api.context import get_ctx
+from api.testbed_model.cpu import LogicalCoreList
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
)
-from framework.testbed_model.cpu import LogicalCoreList
def compute_eal_params(
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index d138727c85..ec539bad95 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,16 +29,16 @@
from paramiko import Channel, channel
from typing_extensions import Self
+from api.context import get_ctx
from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
-from api.context import get_ctx
+from api.testbed_model.node import Node
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node
P = ParamSpec("P")
T = TypeVar("T", bound="InteractiveShell")
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index a0d8039a04..29be7b80fe 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -13,9 +13,9 @@
import textwrap
from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.common import ValidationContext
from framework.test_run import TestRun
-from framework.testbed_model.node import Node
from .config import Configuration, load_config
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 3cecb928ca..5f945163ce 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -36,9 +36,9 @@
from typing_extensions import OrderedDict
from api.exception import DTSError, ErrorSeverity, InternalError
+from api.testbed_model.os_session import OSSessionInfo
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
-from framework.testbed_model.os_session import OSSessionInfo
class Result(IntEnum):
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 605a916ebb..790fbf997d 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,22 +106,22 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
+from api.testbed_model.capability import (
+ Capability,
+ get_supported_capabilities,
+ test_if_supported,
+)
+from api.testbed_model.node import Node
+from api.testbed_model.topology import PortLink, Topology
+from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
-from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
-from framework.testbed_model.capability import (
- Capability,
- get_supported_capabilities,
- test_if_supported,
-)
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import PortLink, Topology
-from framework.testbed_model.traffic_generator import create_traffic_generator
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 6066f7a77a..786cfc7bff 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -30,10 +30,10 @@
from typing_extensions import Self
from api.exception import ConfigurationError, InternalError
+from api.testbed_model.capability import TestProtocol
+from api.testbed_model.topology import Topology
from api.utils import to_pascal_case
from framework.config.common import FrozenModel
-from framework.testbed_model.capability import TestProtocol
-from framework.testbed_model.topology import Topology
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index c57231de22..97e03b8fb7 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,9 +11,9 @@
requires_link_topology,
)
from api.test import verify
+from api.testbed_model.port import Port
from api.testpmd import TestPmd
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.port import Port
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 39784cbcac..fc4b3cb308 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -13,6 +13,7 @@
LinkTopology,
requires_link_topology,
)
+from api.context import get_ctx
from api.cryptodev import Cryptodev
from api.cryptodev.config import (
AeadAlgName,
@@ -31,9 +32,8 @@
)
from api.exception import SkippedTestException
from api.test import verify
-from api.context import get_ctx
+from api.testbed_model.virtual_device import VirtualDevice
from framework.test_suite import BaseConfig, TestSuite, crypto_test
-from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index c018efb1f7..5650366c36 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -13,17 +13,17 @@
requires_link_topology,
requires_nic_capability,
)
+from api.context import filter_cores
from api.packet import (
get_expected_packets,
match_all_packets,
send_packets_and_capture,
)
+from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from api.context import filter_cores
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.cpu import LogicalCoreCount
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 4bd15e2401..042b7019aa 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,15 +36,15 @@
send_packets_and_capture,
)
from api.test import verify
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from api.testpmd import TestPmd
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index fce83604a6..656e2e4bb7 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,12 +19,12 @@
requires_link_topology,
)
from api.test import verify
+from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.linux_session import LinuxSession
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index c57a12c932..91a6d4eb9f 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,11 +18,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
from api.utils import generate_random_packets
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.virtual_device import VirtualDevice
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index bdecdb76fd..c649aac197 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,12 +12,12 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.testbed_model.capability import requires
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.capability import requires
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.virtual_device import VirtualDevice
class TestVirtioFwd(TestSuite):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 5/7] dts: move test suite module from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
` (3 preceding siblings ...)
2026-04-24 17:01 ` [PATCH v3 4/7] dts: move testbed model " Dean Marx
@ 2026-04-24 17:01 ` Dean Marx
2026-04-24 17:01 ` [PATCH v3 6/7] dts: move params directory " Dean Marx
` (4 subsequent siblings)
9 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-24 17:01 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Currently, each test suite imports the TestSuite class
from the DTS framework to use as a base class.
However, the goal for 26.07 is to move all test suite
imports to the API module. Moves and updates the test_suite
file to the API directory, and updates all files that import
test_suite to reflect this change.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.test_suite.rst => api.test_suite.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/capabilities.py | 6 +++---
dts/api/context.py | 2 +-
dts/api/packet.py | 2 +-
dts/{framework => api}/test_suite.py | 3 +--
dts/api/testbed_model/capability.py | 6 +++---
dts/framework/config/__init__.py | 4 ++--
dts/framework/config/test_run.py | 8 ++++----
dts/framework/test_run.py | 2 +-
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
| 2 +-
dts/tests/TestSuite_port_control.py | 2 +-
dts/tests/TestSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 2 +-
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 2 +-
dts/tests/TestSuite_vlan.py | 2 +-
37 files changed, 45 insertions(+), 46 deletions(-)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename dts/{framework => api}/test_suite.py (99%)
diff --git a/doc/api/dts/framework.test_suite.rst b/doc/api/dts/api.test_suite.rst
similarity index 81%
rename from doc/api/dts/framework.test_suite.rst
rename to doc/api/dts/api.test_suite.rst
index 9517f51a4a..4acb7b103a 100644
--- a/doc/api/dts/framework.test_suite.rst
+++ b/doc/api/dts/api.test_suite.rst
@@ -3,6 +3,6 @@
test\_suite - Common Test Suite Features
========================================
-.. automodule:: framework.test_suite
+.. automodule:: api.test_suite
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index f47e4af3f2..7c282bbba1 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -29,7 +29,7 @@ Modules
framework.runner
framework.test_run
- framework.test_suite
+ api.test_suite
framework.test_result
framework.settings
api.context
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 04fc20738b..a4d6b2b424 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -22,7 +22,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -33,7 +33,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -47,7 +47,7 @@ def test_scatter_mbuf_2048(self):
from typing import TYPE_CHECKING, Callable
if TYPE_CHECKING:
- from framework.test_suite import TestProtocol
+ from api.test_suite import TestProtocol
class LinkTopology(IntEnum):
diff --git a/dts/api/context.py b/dts/api/context.py
index 13a2ad6c39..7ed4cc5665 100644
--- a/dts/api/context.py
+++ b/dts/api/context.py
@@ -16,10 +16,10 @@
from framework.settings import SETTINGS
if TYPE_CHECKING:
+ from api.test_suite import TestCase, TestSuite
from api.testbed_model.capability import TestProtocol
from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
- from framework.test_suite import TestCase, TestSuite
P = ParamSpec("P")
diff --git a/dts/api/packet.py b/dts/api/packet.py
index bf90961c26..873b8f0324 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -175,7 +175,7 @@ def adjust_addresses(packets: list[Packet], expected: bool = False) -> list[Pack
Raises:
InternalError: If no tests are running.
"""
- from framework.test_suite import TestSuite
+ from api.test_suite import TestSuite
if get_ctx().local.current_test_suite is None:
raise InternalError("No current test suite, tests aren't running?")
diff --git a/dts/framework/test_suite.py b/dts/api/test_suite.py
similarity index 99%
rename from dts/framework/test_suite.py
rename to dts/api/test_suite.py
index 786cfc7bff..0822f9bfe5 100644
--- a/dts/framework/test_suite.py
+++ b/dts/api/test_suite.py
@@ -34,8 +34,7 @@
from api.testbed_model.topology import Topology
from api.utils import to_pascal_case
from framework.config.common import FrozenModel
-
-from .logger import DTSLogger, get_dts_logger
+from framework.logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
from api.context import Context
diff --git a/dts/api/testbed_model/capability.py b/dts/api/testbed_model/capability.py
index 4e4e976be5..95583261d8 100644
--- a/dts/api/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -25,7 +25,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -36,7 +36,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -71,8 +71,8 @@ def test_scatter_mbuf_2048(self):
from framework.logger import get_dts_logger
if TYPE_CHECKING:
+ from api.test_suite import TestCase
from api.testpmd import TestPmd
- from framework.test_suite import TestCase
P = ParamSpec("P")
TestPmdMethod = Callable[Concatenate["TestPmd", P], Any]
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index 566dc7c4a2..3a3580aaf7 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -43,7 +43,7 @@
# Import only if type checking or building docs, to prevent circular imports.
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig
+ from api.test_suite import BaseConfig
NodesConfig = Annotated[list[NodeConfiguration], Field(min_length=1)]
@@ -182,7 +182,7 @@ def load_config(ctx: ValidationContext) -> Configuration:
nodes = _load_and_parse_model(ctx["settings"].nodes_config_path, NodesConfig, ctx)
try:
- from framework.test_suite import BaseConfig as BaseConfig
+ from api.test_suite import BaseConfig as BaseConfig
Configuration.model_rebuild()
return Configuration.model_validate(
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 977067f42a..81e3dba79b 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -33,7 +33,7 @@
from .common import FrozenModel, load_fields_from_settings
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
+ from api.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
@unique
@@ -230,7 +230,7 @@ class TestSuiteConfig(FrozenModel):
@cached_property
def test_suite_spec(self) -> "TestSuiteSpec":
"""The specification of the requested test suite."""
- from framework.test_suite import find_by_name
+ from api.test_suite import find_by_name
test_suite_spec = find_by_name(self.test_suite_name)
assert (
@@ -280,7 +280,7 @@ def fetch_all_test_suites() -> list[TestSuiteConfig]:
This function does not include the smoke tests.
"""
- from framework.test_suite import AVAILABLE_TEST_SUITES
+ from api.test_suite import AVAILABLE_TEST_SUITES
return [
TestSuiteConfig(test_suite=test_suite.name)
@@ -506,7 +506,7 @@ def filter_tests(
self, tests_config: dict[str, "BaseConfig"]
) -> Iterable[tuple[type["TestSuite"], "BaseConfig", deque[type["TestCase"]]]]:
"""Filter test suites and cases selected for execution."""
- from framework.test_suite import TestCaseType
+ from api.test_suite import TestCaseType
test_suites = [TestSuiteConfig(test_suite="smoke_tests")]
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 790fbf997d..c133fbecb0 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -108,6 +108,7 @@
from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
+from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from api.testbed_model.capability import (
Capability,
get_supported_capabilities,
@@ -121,7 +122,6 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index 97e03b8fb7..31e69c0de9 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,9 +11,9 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.port import Port
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_checksum_offload.py b/dts/tests/TestSuite_checksum_offload.py
index 90ca798e56..a2ea13991b 100644
--- a/dts/tests/TestSuite_checksum_offload.py
+++ b/dts/tests/TestSuite_checksum_offload.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import ChecksumOffloadOptions, PacketOffloadFlag
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_IPV4_CKSUM)
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index fc4b3cb308..d2a6cbab94 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -32,8 +32,8 @@
)
from api.exception import SkippedTestException
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
-from framework.test_suite import BaseConfig, TestSuite, crypto_test
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_dual_vlan.py b/dts/tests/TestSuite_dual_vlan.py
index 1b77dd2b47..f3347a6d52 100644
--- a/dts/tests/TestSuite_dual_vlan.py
+++ b/dts/tests/TestSuite_dual_vlan.py
@@ -21,9 +21,9 @@
from api.capabilities import LinkTopology, requires_link_topology
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestDualVlan(TestSuite):
diff --git a/dts/tests/TestSuite_dynamic_config.py b/dts/tests/TestSuite_dynamic_config.py
index 7204ec4f73..b9e2c30da1 100644
--- a/dts/tests/TestSuite_dynamic_config.py
+++ b/dts/tests/TestSuite_dynamic_config.py
@@ -27,9 +27,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index b62efa2b42..24584c7d60 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -38,9 +38,9 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
def setup_and_teardown_test(
diff --git a/dts/tests/TestSuite_hello_world.py b/dts/tests/TestSuite_hello_world.py
index bf1a93c782..cd62eb8f3e 100644
--- a/dts/tests/TestSuite_hello_world.py
+++ b/dts/tests/TestSuite_hello_world.py
@@ -9,8 +9,8 @@
"""
from api.test import log
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 5650366c36..f237821a04 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -19,11 +19,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index b44822d31c..eb1413f336 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -26,8 +26,8 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mtu.py b/dts/tests/TestSuite_mtu.py
index 8355495d33..c264db299e 100644
--- a/dts/tests/TestSuite_mtu.py
+++ b/dts/tests/TestSuite_mtu.py
@@ -23,8 +23,8 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
STANDARD_FRAME = 1518 # --max-pkt-len will subtract l2 information at a minimum of 18 bytes.
JUMBO_FRAME = 9018
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 042b7019aa..fd5cef5268 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,6 +36,7 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -44,7 +45,6 @@
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
-from framework.test_suite import TestSuite, func_test
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py
index 96da67ee7d..6c7f6d79fe 100644
--- a/dts/tests/TestSuite_pmd_buffer_scatter.py
+++ b/dts/tests/TestSuite_pmd_buffer_scatter.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 4df273e3e1..162e08ccbc 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -23,6 +23,7 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import (
@@ -31,7 +32,6 @@
TestPmdVerbosePacket,
)
from api.utils import StrEnum
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_port_control.py b/dts/tests/TestSuite_port_control.py
index 6be47838d0..5b960cb3a3 100644
--- a/dts/tests/TestSuite_port_control.py
+++ b/dts/tests/TestSuite_port_control.py
@@ -18,9 +18,9 @@
)
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_port_restart_config_persistency.py b/dts/tests/TestSuite_port_restart_config_persistency.py
index 4ea22b6d70..88df35d33c 100644
--- a/dts/tests/TestSuite_port_restart_config_persistency.py
+++ b/dts/tests/TestSuite_port_restart_config_persistency.py
@@ -14,9 +14,9 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import TestPmdPortFlowCtrl
-from framework.test_suite import TestSuite, func_test
ALTERNATIVE_MTU: int = 800
STANDARD_MTU: int = 1500
diff --git a/dts/tests/TestSuite_port_stats.py b/dts/tests/TestSuite_port_stats.py
index 3dc045f847..0328c6718c 100644
--- a/dts/tests/TestSuite_port_stats.py
+++ b/dts/tests/TestSuite_port_stats.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_promisc_support.py b/dts/tests/TestSuite_promisc_support.py
index a0c65dc662..c59c8c6078 100644
--- a/dts/tests/TestSuite_promisc_support.py
+++ b/dts/tests/TestSuite_promisc_support.py
@@ -21,8 +21,8 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_qinq.py b/dts/tests/TestSuite_qinq.py
index 505d71dbc8..5dde37d4db 100644
--- a/dts/tests/TestSuite_qinq.py
+++ b/dts/tests/TestSuite_qinq.py
@@ -18,8 +18,8 @@
from api.capabilities import NicCapability, requires_nic_capability
from api.packet import send_packet_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestQinq(TestSuite):
diff --git a/dts/tests/TestSuite_queue_start_stop.py b/dts/tests/TestSuite_queue_start_stop.py
index e9048d4245..6935f395c1 100644
--- a/dts/tests/TestSuite_queue_start_stop.py
+++ b/dts/tests/TestSuite_queue_start_stop.py
@@ -24,9 +24,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.TWO_LINKS)
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 7e50a075ac..8c5c59edec 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import fail, log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import FlowRule
-from framework.test_suite import TestSuite, func_test
@dataclass
diff --git a/dts/tests/TestSuite_rx_tx_offload.py b/dts/tests/TestSuite_rx_tx_offload.py
index b0da627d3c..c8d24baaae 100644
--- a/dts/tests/TestSuite_rx_tx_offload.py
+++ b/dts/tests/TestSuite_rx_tx_offload.py
@@ -13,12 +13,12 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import (
OffloadConfiguration,
RxTxLiteralSwitch,
)
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.ONE_LINK)
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index 1e7ab7b036..f1eb435759 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -22,10 +22,10 @@
)
from api.packet import assess_performance_by_packet
from api.test import verify, write_performance_json
+from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
from framework.params.types import TestPmdParamsDict
-from framework.test_suite import BaseConfig, TestSuite, perf_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 656e2e4bb7..b3eb325fc0 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,12 +19,12 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index 91a6d4eb9f..05a6d3aa18 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,11 +18,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
from api.utils import generate_random_packets
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_uni_pkt.py b/dts/tests/TestSuite_uni_pkt.py
index 222276ce67..d83185d1b2 100644
--- a/dts/tests/TestSuite_uni_pkt.py
+++ b/dts/tests/TestSuite_uni_pkt.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
class TestUniPkt(TestSuite):
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index c649aac197..2c10478df3 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,12 +12,12 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import requires
from api.testbed_model.linux_session import LinuxSession
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestVirtioFwd(TestSuite):
diff --git a/dts/tests/TestSuite_vlan.py b/dts/tests/TestSuite_vlan.py
index 898673fc86..975e87b128 100644
--- a/dts/tests/TestSuite_vlan.py
+++ b/dts/tests/TestSuite_vlan.py
@@ -23,9 +23,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_VLAN_FILTER)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 6/7] dts: move params directory from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
` (4 preceding siblings ...)
2026-04-24 17:01 ` [PATCH v3 5/7] dts: move test suite module " Dean Marx
@ 2026-04-24 17:01 ` Dean Marx
2026-04-24 17:01 ` [PATCH v3 7/7] dts: separate Linux session into interface and logic Dean Marx
` (3 subsequent siblings)
9 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-24 17:01 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The params directory is imported in test suites such as
packet capture to use as a base class for dumpcap.
Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.params.eal.rst => api.params.eal.rst} | 2 +-
doc/api/dts/{framework.params.rst => api.params.rst} | 6 +++---
.../{framework.params.types.rst => api.params.types.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/api/cryptodev/config.py | 4 ++--
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 2 +-
dts/{framework => api}/params/types.py | 8 ++++++--
dts/api/testpmd/__init__.py | 7 ++++++-
dts/api/testpmd/config.py | 6 +++---
dts/framework/remote_session/blocking_app.py | 4 ++--
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
17 files changed, 32 insertions(+), 23 deletions(-)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (97%)
rename dts/{framework => api}/params/types.py (97%)
diff --git a/doc/api/dts/framework.params.eal.rst b/doc/api/dts/api.params.eal.rst
similarity index 79%
rename from doc/api/dts/framework.params.eal.rst
rename to doc/api/dts/api.params.eal.rst
index 6999b00233..4531cb1fe1 100644
--- a/doc/api/dts/framework.params.eal.rst
+++ b/doc/api/dts/api.params.eal.rst
@@ -3,6 +3,6 @@
eal - EAL Parameters Modelling
==============================
-.. automodule:: framework.params.eal
+.. automodule:: api.params.eal
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.params.rst b/doc/api/dts/api.params.rst
similarity index 71%
rename from doc/api/dts/framework.params.rst
rename to doc/api/dts/api.params.rst
index d8c6af9667..3ea7f9215e 100644
--- a/doc/api/dts/framework.params.rst
+++ b/doc/api/dts/api.params.rst
@@ -3,7 +3,7 @@
params - Command Line Parameters Modelling
==========================================
-.. automodule:: framework.params
+.. automodule:: api.params
:members:
:show-inheritance:
@@ -11,5 +11,5 @@ params - Command Line Parameters Modelling
:hidden:
:maxdepth: 1
- framework.params.eal
- framework.params.types
+ api.params.eal
+ api.params.types
diff --git a/doc/api/dts/framework.params.types.rst b/doc/api/dts/api.params.types.rst
similarity index 80%
rename from doc/api/dts/framework.params.types.rst
rename to doc/api/dts/api.params.types.rst
index 6d609038be..4754b3a665 100644
--- a/doc/api/dts/framework.params.types.rst
+++ b/doc/api/dts/api.params.types.rst
@@ -3,6 +3,6 @@
params.types - Parameters Modelling Types
=========================================
-.. automodule:: framework.params.types
+.. automodule:: api.params.types
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 7c282bbba1..e89e782ac0 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -18,7 +18,7 @@ Packages
api
api.testbed_model
framework.remote_session
- framework.params
+ api.params
framework.config
Modules
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 1ba8e0d977..bbfe3622c2 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -27,7 +27,7 @@
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
- from framework.params.types import CryptoPmdParamsDict
+ from api.params.types import CryptoPmdParamsDict
from pathlib import PurePath
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index a88e70d45c..3420c2fe91 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.params import Params, Switch
+from api.params.eal import EalParams
from api.utils import StrEnum
-from framework.params import Params, Switch
-from framework.params.eal import EalParams
Silent = Literal[""]
diff --git a/dts/framework/params/__init__.py b/dts/api/params/__init__.py
similarity index 100%
rename from dts/framework/params/__init__.py
rename to dts/api/params/__init__.py
diff --git a/dts/framework/params/eal.py b/dts/api/params/eal.py
similarity index 97%
rename from dts/framework/params/eal.py
rename to dts/api/params/eal.py
index 86bfd3fcc6..64fa45ae12 100644
--- a/dts/framework/params/eal.py
+++ b/dts/api/params/eal.py
@@ -6,9 +6,9 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.params import Params, Switch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.virtual_device import VirtualDevice
-from framework.params import Params, Switch
if TYPE_CHECKING:
from api.testbed_model.port import Port
diff --git a/dts/framework/params/types.py b/dts/api/params/types.py
similarity index 97%
rename from dts/framework/params/types.py
rename to dts/api/params/types.py
index f2fa69f8b8..2c215cfe54 100644
--- a/dts/framework/params/types.py
+++ b/dts/api/params/types.py
@@ -12,8 +12,10 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
params = TestPmdParams(**kwargs)
"""
+from __future__ import annotations
+
from pathlib import PurePath
-from typing import TypedDict
+from typing import TYPE_CHECKING, TypedDict
from api.cryptodev.config import (
AeadAlgName,
@@ -56,7 +58,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TXRingParams,
TxUDPPortPair,
)
-from framework.params import Switch, YesNoSwitch
+
+if TYPE_CHECKING:
+ from api.params import Switch, YesNoSwitch
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9f47a15433..85ebe2ea65 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -14,6 +14,8 @@
testpmd.close()
"""
+from __future__ import annotations
+
import functools
import re
import time
@@ -21,6 +23,7 @@
from enum import Flag
from pathlib import PurePath
from typing import (
+ TYPE_CHECKING,
Any,
Callable,
ClassVar,
@@ -56,7 +59,9 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.params.types import TestPmdParamsDict
+
+if TYPE_CHECKING:
+ from api.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
from framework.settings import SETTINGS
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index 8b688834ee..96fe5e79fb 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,8 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
-from api.utils import StrEnum
-from framework.params import (
+from api.params import (
Params,
Switch,
YesNoSwitch,
@@ -24,7 +23,8 @@
modify_str,
str_from_flag_value,
)
-from framework.params.eal import EalParams
+from api.params.eal import EalParams
+from api.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 84db3974b1..537d937eca 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,9 +31,9 @@
from typing_extensions import Self
from api.context import get_ctx
+from api.params import Params
+from api.params.eal import EalParams
from api.testbed_model.node import Node
-from framework.params import Params
-from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 713a564d25..afdf7526d9 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -15,6 +15,7 @@
from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
@@ -32,7 +33,6 @@
RemoteDPDKTreeLocation,
)
from framework.logger import DTSLogger, get_dts_logger
-from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index b807f9bdae..61cc4687f3 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,8 +11,8 @@
from pathlib import PurePath
from api.context import get_ctx
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCoreList
-from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index ec539bad95..f7f0669eea 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -35,9 +35,9 @@
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from api.params import Params
from api.testbed_model.node import Node
from framework.logger import DTSLogger, get_dts_logger
-from framework.params import Params
from framework.settings import SETTINGS
P = ParamSpec("P")
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index fd5cef5268..ba67c9e1c6 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -35,6 +35,7 @@
match_all_packets,
send_packets_and_capture,
)
+from api.params import Params
from api.test import verify
from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
@@ -42,7 +43,6 @@
PacketFilteringConfig,
)
from api.testpmd import TestPmd
-from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index f1eb435759..24f2cebf17 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -21,11 +21,11 @@
requires_link_topology,
)
from api.packet import assess_performance_by_packet
+from api.params.types import TestPmdParamsDict
from api.test import verify, write_performance_json
from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
-from framework.params.types import TestPmdParamsDict
class Config(BaseConfig):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 7/7] dts: separate Linux session into interface and logic
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
` (5 preceding siblings ...)
2026-04-24 17:01 ` [PATCH v3 6/7] dts: move params directory " Dean Marx
@ 2026-04-24 17:01 ` Dean Marx
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (2 subsequent siblings)
9 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-24 17:01 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Separate Linux session into an interface for the API,
and a logical module in the framework.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/framework.linux_session.rst | 6 +
doc/api/dts/index.rst | 1 +
dts/api/testbed_model/linux_session.py | 372 ++----------------------
dts/api/testbed_model/node.py | 2 +-
dts/framework/linux_session.py | 366 +++++++++++++++++++++++
5 files changed, 397 insertions(+), 350 deletions(-)
create mode 100644 doc/api/dts/framework.linux_session.rst
create mode 100644 dts/framework/linux_session.py
diff --git a/doc/api/dts/framework.linux_session.rst b/doc/api/dts/framework.linux_session.rst
new file mode 100644
index 0000000000..0bbbe36eb7
--- /dev/null
+++ b/doc/api/dts/framework.linux_session.rst
@@ -0,0 +1,6 @@
+framework.linux\_session
+========================
+
+.. automodule:: framework.linux_session
+ :members:
+ :show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index e89e782ac0..0dbc18b75c 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -37,6 +37,7 @@ Modules
framework.parser
api.utils
api.exception
+ framework.linux_session
Indices and tables
diff --git a/dts/api/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
index 7307b2abe2..5bcbf1ce97 100644
--- a/dts/api/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -1,367 +1,41 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2023 PANTHEON.tech s.r.o.
# Copyright(c) 2023 University of New Hampshire
+"""Linux OS session interface.
-"""Linux OS translator.
-
-Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
-compliant with POSIX standards, so this module only implements the parts that aren't.
-This intermediate module implements the common parts of mostly POSIX compliant distributions.
+Extends the base :class:`~.os_session.OSSession` with methods specific to Linux nodes.
+The concrete implementation containing all backend logic lives in the framework package.
"""
-import json
-import re
-from collections.abc import Iterable
-from functools import cached_property
+from abc import ABC, abstractmethod
from pathlib import PurePath
-from typing import TypedDict
-
-from typing_extensions import NotRequired
-
-from api.exception import (
- ConfigurationError,
- InternalError,
- RemoteCommandExecutionError,
-)
-from api.testbed_model.port import PortInfo
-from api.utils import expand_range
-
-from .cpu import LogicalCore
-from .port import Port
-from .posix_session import PosixSession
-
-
-class LshwConfigurationOutput(TypedDict):
- """The relevant parts of ``lshw``'s ``configuration`` section."""
-
- #:
- driver: str
- #:
- link: str
-
-
-class LshwOutput(TypedDict):
- """A model of the relevant information from ``lshw``'s json output.
-
- Example:
- ::
-
- {
- ...
- "businfo" : "pci@0000:08:00.0",
- "logicalname" : "enp8s0",
- "version" : "00",
- "serial" : "52:54:00:59:e1:ac",
- ...
- "configuration" : {
- ...
- "link" : "yes",
- ...
- },
- ...
- """
-
- #:
- businfo: str
- #:
- logicalname: NotRequired[str]
- #:
- serial: NotRequired[str]
- #:
- configuration: LshwConfigurationOutput
-
-
-class LinuxSession(PosixSession):
- """The implementation of non-Posix compliant parts of Linux."""
-
- @staticmethod
- def _get_privileged_command(command: str) -> str:
- command = command.replace(r"'", r"\'")
- return f"sudo -- sh -c '{command}'"
- def get_remote_cpus(self) -> list[LogicalCore]:
- """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
- cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
- lcores = []
- for cpu_line in cpu_info.splitlines():
- lcore, core, socket, node = map(int, cpu_line.split(","))
- lcores.append(LogicalCore(lcore, core, socket, node))
- return lcores
-
- def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
- """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
- return dpdk_prefix
-
- def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
-
- Raises:
- ConfigurationError: If the given `hugepage_size` is not supported by the OS.
- """
- self._logger.info("Getting Hugepage information.")
- if (
- f"hugepages-{hugepage_size}kB"
- not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
- ):
- raise ConfigurationError("hugepage size not supported by operating system")
- hugepages_total = self._get_hugepages_total(hugepage_size)
- self._numa_nodes = self._get_numa_nodes()
-
- if force_first_numa or hugepages_total < number_of:
- # when forcing numa, we need to clear existing hugepages regardless
- # of size, so they can be moved to the first numa node
- self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
- else:
- self._logger.info("Hugepages already configured.")
- self._mount_huge_pages()
-
- def _get_hugepages_total(self, hugepage_size: int) -> int:
- hugepages_total = self.send_command(
- f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
- ).stdout
- return int(hugepages_total)
-
- def _get_numa_nodes(self) -> list[int]:
- try:
- numa_count = self.send_command(
- "cat /sys/devices/system/node/online", verify=True
- ).stdout
- numa_range = expand_range(numa_count)
- except RemoteCommandExecutionError:
- # the file doesn't exist, meaning the node doesn't support numa
- numa_range = []
- return numa_range
-
- def _mount_huge_pages(self) -> None:
- self._logger.info("Re-mounting Hugepages.")
- hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
- self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
- result = self.send_command(hugapge_fs_cmd)
- if result.stdout == "":
- remote_mount_path = "/mnt/huge"
- self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
- self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
-
- def _supports_numa(self) -> bool:
- # the system supports numa if self._numa_nodes is non-empty and there are more
- # than one numa node (in the latter case it may actually support numa, but
- # there's no reason to do any numa specific configuration)
- return len(self._numa_nodes) > 1
-
- def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
- self._logger.info("Configuring Hugepages.")
- hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
- if force_first_numa and self._supports_numa():
- # clear non-numa hugepages
- self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
- hugepage_config_path = (
- f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
- f"/hugepages-{size}kB/nr_hugepages"
- )
-
- self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
-
- def get_port_info(self, pci_address: str) -> PortInfo:
- """Overrides :meth:`~.os_session.OSSession.get_port_info`.
-
- Raises:
- ConfigurationError: If the port could not be found.
- """
- bus_info = f"pci@{pci_address}"
- port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
- if port is None:
- raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
- logical_name = port.get("logicalname", "")
- mac_address = port.get("serial", "")
+class LinuxSession(ABC):
+ """Abstract interface for Linux-specific OS session operations."""
- configuration = port.get("configuration", {})
- driver = configuration.get("driver", "")
- is_link_up = configuration.get("link", "down") == "up"
-
- return PortInfo(mac_address, logical_name, driver, is_link_up)
-
- def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
-
- The :attr:`~.devbind_script_path` property must be setup in order to call this method.
- """
- ports_pci_addrs = " ".join(port.pci for port in ports)
-
- self.send_command(
- f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
- privileged=True,
- verify=True,
- )
-
- del self._lshw_net_info
-
- def bring_up_link(self, ports: Iterable[Port]) -> None:
- """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
- for port in ports:
- self.send_command(
- f"ip link set dev {port.logical_name} up", privileged=True, verify=True
- )
-
- del self._lshw_net_info
-
- def set_interface_link_up(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
- self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
-
- def delete_interface(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
- self.send_command(f"ip link delete {name}", privileged=True)
-
- @cached_property
+ @property
+ @abstractmethod
def devbind_script_path(self) -> PurePath:
- """The path to the dpdk-devbind.py script on the node.
-
- Needs to be manually assigned first in order to be used.
+ """The path to the devbind script."""
- Raises:
- InternalError: If accessed before environment setup.
- """
- raise InternalError("Accessed devbind script path before setup.")
-
- def load_vfio(self, pf_port: Port) -> None:
- """Overrides :meth:`~os_session.OSSession,load_vfio`."""
- cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
- device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
- if device and device.group(1) in ["37c8", "0435", "19e2"]:
- self.send_command(
- "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
- privileged=True,
- )
- self.send_command(
- "modprobe -r vfio_virqfd; modprobe -r vfio",
- privileged=True,
- )
- self.send_command(
- "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
- )
- self.send_command(
- "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
- privileged=True,
- )
- else:
- self.send_command("modprobe vfio-pci")
- self.refresh_lshw()
+ @devbind_script_path.setter
+ @abstractmethod
+ def devbind_script_path(self, value: PurePath) -> None:
+ """Set the devbind script path after environment setup."""
- def create_crypto_vfs(self, pf_port: list[Port]) -> None:
- """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+ @abstractmethod
+ def set_interface_link_up(self, name: str) -> None:
+ """Set the link status of an interface to up.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ name: The name of the interface.
"""
- for port in pf_port:
- self.delete_crypto_vfs(port)
- for port in pf_port:
- sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
- )
- self.send_command(
- f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
- )
-
- self.refresh_lshw()
- def create_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+ @abstractmethod
+ def delete_interface(self, name: str) -> None:
+ """Delete a virtual interface.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ name: The name of the interface to delete.
"""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- if curr_num_vfs == 0:
- self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
- self.refresh_lshw()
-
- def delete_crypto_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
- self.send_command(
- f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
- privileged=True,
- )
- self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
-
- def delete_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if curr_num_vfs == 0:
- self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
- else:
- self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
-
- def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- f"readlink {sys_bus_path}/virtfn*",
- privileged=True,
- )
- return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
- return []
-
- def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
- + f"{sys_bus_path}/virtfn*/uevent",
- privileged=True,
- )
- return pci_addrs.stdout.splitlines()
- else:
- return []
-
- @cached_property
- def _lshw_net_info(self) -> list[LshwOutput]:
- output = self.send_command("lshw -quiet -json -C network", verify=True)
- return json.loads(output.stdout)
-
- def refresh_lshw(self) -> None:
- """Force refresh of cached lshw network info."""
- if "_lshw_net_info" in self.__dict__:
- del self.__dict__["_lshw_net_info"]
- _ = self._lshw_net_info
-
- def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
- if attr_value:
- setattr(port, attr_name, attr_value)
- self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
- else:
- self._logger.warning(
- f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
- )
-
- def configure_port_mtu(self, mtu: int, port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
- self.send_command(
- f"ip link set dev {port.logical_name} mtu {mtu}",
- privileged=True,
- verify=True,
- )
-
- def configure_ipv4_forwarding(self, enable: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
- state = 1 if enable else 0
- self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
diff --git a/dts/api/testbed_model/node.py b/dts/api/testbed_model/node.py
index 40dd7f0666..ae9886e531 100644
--- a/dts/api/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -22,10 +22,10 @@
OS,
NodeConfiguration,
)
+from framework.linux_session import LinuxSession
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
-from .linux_session import LinuxSession
from .os_session import OSSession, OSSessionInfo
from .port import Port
diff --git a/dts/framework/linux_session.py b/dts/framework/linux_session.py
new file mode 100644
index 0000000000..e5320b7fc4
--- /dev/null
+++ b/dts/framework/linux_session.py
@@ -0,0 +1,366 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2023 University of New Hampshire
+
+"""Linux OS translator.
+
+Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
+compliant with POSIX standards, so this module only implements the parts that aren't.
+This intermediate module implements the common parts of mostly POSIX compliant distributions.
+"""
+
+import json
+import re
+from collections.abc import Iterable
+from functools import cached_property
+from pathlib import PurePath
+from typing import TypedDict
+
+from typing_extensions import NotRequired
+
+from api.exception import (
+ ConfigurationError,
+ InternalError,
+ RemoteCommandExecutionError,
+)
+from api.testbed_model.cpu import LogicalCore
+from api.testbed_model.linux_session import LinuxSession as LinuxSessionBase
+from api.testbed_model.port import Port, PortInfo
+from api.testbed_model.posix_session import PosixSession
+from api.utils import expand_range
+
+
+class LshwConfigurationOutput(TypedDict):
+ """The relevant parts of ``lshw``'s ``configuration`` section."""
+
+ #:
+ driver: str
+ #:
+ link: str
+
+
+class LshwOutput(TypedDict):
+ """A model of the relevant information from ``lshw``'s json output.
+
+ Example:
+ ::
+
+ {
+ ...
+ "businfo" : "pci@0000:08:00.0",
+ "logicalname" : "enp8s0",
+ "version" : "00",
+ "serial" : "52:54:00:59:e1:ac",
+ ...
+ "configuration" : {
+ ...
+ "link" : "yes",
+ ...
+ },
+ ...
+ """
+
+ #:
+ businfo: str
+ #:
+ logicalname: NotRequired[str]
+ #:
+ serial: NotRequired[str]
+ #:
+ configuration: LshwConfigurationOutput
+
+
+class LinuxSession(PosixSession, LinuxSessionBase):
+ """The implementation of non-Posix compliant parts of Linux."""
+
+ @staticmethod
+ def _get_privileged_command(command: str) -> str:
+ command = command.replace(r"'", r"\'")
+ return f"sudo -- sh -c '{command}'"
+
+ def get_remote_cpus(self) -> list[LogicalCore]:
+ """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
+ cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
+ lcores = []
+ for cpu_line in cpu_info.splitlines():
+ lcore, core, socket, node = map(int, cpu_line.split(","))
+ lcores.append(LogicalCore(lcore, core, socket, node))
+ return lcores
+
+ def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
+ """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
+ return dpdk_prefix
+
+ def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
+
+ Raises:
+ ConfigurationError: If the given `hugepage_size` is not supported by the OS.
+ """
+ self._logger.info("Getting Hugepage information.")
+ if (
+ f"hugepages-{hugepage_size}kB"
+ not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
+ ):
+ raise ConfigurationError("hugepage size not supported by operating system")
+ hugepages_total = self._get_hugepages_total(hugepage_size)
+ self._numa_nodes = self._get_numa_nodes()
+
+ if force_first_numa or hugepages_total < number_of:
+ # when forcing numa, we need to clear existing hugepages regardless
+ # of size, so they can be moved to the first numa node
+ self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
+ else:
+ self._logger.info("Hugepages already configured.")
+ self._mount_huge_pages()
+
+ def _get_hugepages_total(self, hugepage_size: int) -> int:
+ hugepages_total = self.send_command(
+ f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
+ ).stdout
+ return int(hugepages_total)
+
+ def _get_numa_nodes(self) -> list[int]:
+ try:
+ numa_count = self.send_command(
+ "cat /sys/devices/system/node/online", verify=True
+ ).stdout
+ numa_range = expand_range(numa_count)
+ except RemoteCommandExecutionError:
+ # the file doesn't exist, meaning the node doesn't support numa
+ numa_range = []
+ return numa_range
+
+ def _mount_huge_pages(self) -> None:
+ self._logger.info("Re-mounting Hugepages.")
+ hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
+ self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
+ result = self.send_command(hugapge_fs_cmd)
+ if result.stdout == "":
+ remote_mount_path = "/mnt/huge"
+ self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
+ self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
+
+ def _supports_numa(self) -> bool:
+ # the system supports numa if self._numa_nodes is non-empty and there are more
+ # than one numa node (in the latter case it may actually support numa, but
+ # there's no reason to do any numa specific configuration)
+ return len(self._numa_nodes) > 1
+
+ def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
+ self._logger.info("Configuring Hugepages.")
+ hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
+ if force_first_numa and self._supports_numa():
+ # clear non-numa hugepages
+ self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
+ hugepage_config_path = (
+ f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
+ f"/hugepages-{size}kB/nr_hugepages"
+ )
+
+ self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
+
+ def get_port_info(self, pci_address: str) -> PortInfo:
+ """Overrides :meth:`~.os_session.OSSession.get_port_info`.
+
+ Raises:
+ ConfigurationError: If the port could not be found.
+ """
+ bus_info = f"pci@{pci_address}"
+ port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
+ if port is None:
+ raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
+
+ logical_name = port.get("logicalname", "")
+ mac_address = port.get("serial", "")
+
+ configuration = port.get("configuration", {})
+ driver = configuration.get("driver", "")
+ is_link_up = configuration.get("link", "down") == "up"
+
+ return PortInfo(mac_address, logical_name, driver, is_link_up)
+
+ def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
+
+ The :attr:`~.devbind_script_path` property must be setup in order to call this method.
+ """
+ ports_pci_addrs = " ".join(port.pci for port in ports)
+
+ self.send_command(
+ f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
+ privileged=True,
+ verify=True,
+ )
+
+ del self._lshw_net_info
+
+ def bring_up_link(self, ports: Iterable[Port]) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
+ for port in ports:
+ self.send_command(
+ f"ip link set dev {port.logical_name} up", privileged=True, verify=True
+ )
+
+ del self._lshw_net_info
+
+ def set_interface_link_up(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
+ self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
+
+ def delete_interface(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
+ self.send_command(f"ip link delete {name}", privileged=True)
+
+ @cached_property
+ def devbind_script_path(self) -> PurePath:
+ """The path to the dpdk-devbind.py script on the node.
+
+ Needs to be manually assigned first in order to be used.
+
+ Raises:
+ InternalError: If accessed before environment setup.
+ """
+ raise InternalError("Accessed devbind script path before setup.")
+
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~os_session.OSSession,load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self.refresh_lshw()
+
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+
+ self.refresh_lshw()
+
+ def create_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ if curr_num_vfs == 0:
+ self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ self.refresh_lshw()
+
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
+ def delete_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if curr_num_vfs == 0:
+ self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
+ else:
+ self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
+ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
+ + f"{sys_bus_path}/virtfn*/uevent",
+ privileged=True,
+ )
+ return pci_addrs.stdout.splitlines()
+ else:
+ return []
+
+ @cached_property
+ def _lshw_net_info(self) -> list[LshwOutput]:
+ output = self.send_command("lshw -quiet -json -C network", verify=True)
+ return json.loads(output.stdout)
+
+ def refresh_lshw(self) -> None:
+ """Force refresh of cached lshw network info."""
+ if "_lshw_net_info" in self.__dict__:
+ del self.__dict__["_lshw_net_info"]
+ _ = self._lshw_net_info
+
+ def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
+ if attr_value:
+ setattr(port, attr_name, attr_value)
+ self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
+ else:
+ self._logger.warning(
+ f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
+ )
+
+ def configure_port_mtu(self, mtu: int, port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
+ self.send_command(
+ f"ip link set dev {port.logical_name} mtu {mtu}",
+ privileged=True,
+ verify=True,
+ )
+
+ def configure_ipv4_forwarding(self, enable: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
+ state = 1 if enable else 0
+ self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 0/7] dts: move test suite imports from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
` (6 preceding siblings ...)
2026-04-24 17:01 ` [PATCH v3 7/7] dts: separate Linux session into interface and logic Dean Marx
@ 2026-04-28 18:08 ` Dean Marx
2026-04-28 18:08 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
` (6 more replies)
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
9 siblings, 7 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-28 18:08 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
This series moves various modules from the framework directory
to the API based on which are being imported by test suites.
These include:
- test_suite
- testbed_model
- exception
- utils
- context
- params
This eliminates all test suite framework imports except for
the remote_session imports in packet_capture, as well
as the settings/config imports in smoke_tests. I believe these
imports, and what to do with them, should be a topic of discussion
in future DTS meetings, as I don't believe they should reside in the
API, even if they are being imported in test suites.
In addition to these changes, I've split the linux_session module
into an interface in api/testbed_model/linux_session and an
implementation in framework/linux_session. This way, users
can still import linux session if necessary, without exposing
the backend implementation.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
v2:
* Integrated rst updates with corresponding commits for build testing
v3:
* Fixed commit ordering to resolve import inconsistency
* Updated Linux Session interface with test suite methods
v4:
* Added missing SPDX tag to framework.linux_session.rst
* Added lazy linux session import in node.py to
resolve circular import issue
Dean Marx (7):
dts: move exception module from framework to API
dts: move utils from framework to API
dts: move context from framework to API
dts: move testbed model from framework to API
dts: move test suite module from framework to API
dts: move params directory from framework to API
dts: separate Linux session into interface and logic
...{framework.context.rst => api.context.rst} | 2 +-
...mework.exception.rst => api.exception.rst} | 2 +-
...work.params.eal.rst => api.params.eal.rst} | 2 +-
.../{framework.params.rst => api.params.rst} | 6 +--
....params.types.rst => api.params.types.rst} | 2 +-
...work.test_suite.rst => api.test_suite.rst} | 2 +-
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 ++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
.../{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/framework.linux_session.rst | 8 ++++
doc/api/dts/framework.testbed_model.rst | 28 -------------
...mework.testbed_model.traffic_generator.rst | 16 --------
doc/api/dts/index.rst | 13 +++---
dts/api/artifact.py | 6 +--
dts/api/capabilities.py | 14 +++----
dts/{framework => api}/context.py | 14 +++----
dts/api/cryptodev/__init__.py | 6 +--
dts/api/cryptodev/config.py | 6 +--
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 18 ++++----
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 8 ++--
dts/{framework => api}/params/types.py | 14 ++++---
dts/api/test.py | 4 +-
dts/{framework => api}/test_suite.py | 15 ++++---
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 18 ++++----
dts/{framework => api}/testbed_model/cpu.py | 4 +-
dts/api/testbed_model/linux_session.py | 41 +++++++++++++++++++
dts/{framework => api}/testbed_model/node.py | 12 +++---
.../testbed_model/os_session.py | 8 ++--
dts/{framework => api}/testbed_model/port.py | 2 +-
.../testbed_model/posix_session.py | 6 +--
.../testbed_model/topology.py | 20 ++++-----
.../traffic_generator/__init__.py | 4 +-
.../capturing_traffic_generator.py | 4 +-
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 14 +++----
.../traffic_generator/traffic_generator.py | 8 ++--
.../testbed_model/traffic_generator/trex.py | 16 ++++----
.../testbed_model/virtual_device.py | 0
dts/api/testpmd/__init__.py | 11 +++--
dts/api/testpmd/config.py | 6 +--
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 2 +-
dts/framework/config/__init__.py | 6 +--
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 12 +++---
.../{testbed_model => }/linux_session.py | 15 ++++---
dts/framework/parser.py | 2 +-
dts/framework/remote_session/blocking_app.py | 8 ++--
dts/framework/remote_session/dpdk.py | 16 ++++----
dts/framework/remote_session/dpdk_shell.py | 6 +--
.../interactive_remote_session.py | 2 +-
.../remote_session/interactive_shell.py | 8 ++--
.../remote_session/remote_session.py | 4 +-
dts/framework/runner.py | 4 +-
dts/framework/test_result.py | 5 +--
dts/framework/test_run.py | 24 +++++------
dts/tests/TestSuite_blocklist.py | 4 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 8 ++--
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 4 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 8 ++--
dts/tests/TestSuite_mac_filter.py | 4 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 12 +++---
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
dts/tests/TestSuite_pmd_rss.py | 6 +--
dts/tests/TestSuite_port_control.py | 2 +-
...stSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 12 +++---
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
.../TestSuite_single_core_forward_perf.py | 4 +-
dts/tests/TestSuite_smoke_tests.py | 6 +--
dts/tests/TestSuite_softnic.py | 6 +--
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 8 ++--
dts/tests/TestSuite_vlan.py | 2 +-
98 files changed, 359 insertions(+), 301 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
create mode 100644 doc/api/dts/framework.linux_session.rst
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/context.py (90%)
rename dts/{framework => api}/exception.py (100%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (89%)
rename dts/{framework => api}/params/types.py (94%)
rename dts/{framework => api}/test_suite.py (98%)
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (98%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
create mode 100644 dts/api/testbed_model/linux_session.py
rename dts/{framework => api}/testbed_model/node.py (96%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (98%)
rename dts/{framework => api}/testbed_model/posix_session.py (99%)
rename dts/{framework => api}/testbed_model/topology.py (96%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (95%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (91%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (96%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
rename dts/{framework => api}/utils.py (99%)
rename dts/framework/{testbed_model => }/linux_session.py (97%)
--
2.52.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v4 1/7] dts: move exception module from framework to API
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
@ 2026-04-28 18:08 ` Dean Marx
2026-04-28 18:08 ` [PATCH v4 2/7] dts: move utils " Dean Marx
` (5 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-28 18:08 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites currently import the exception module
from the framework in order to catch certain errors during
test execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.exception.rst => api.exception.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 2 +-
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/config/__init__.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/context.py | 2 +-
dts/framework/parser.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
.../remote_session/interactive_remote_session.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 4 ++--
dts/framework/remote_session/remote_session.py | 4 ++--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 3 +--
dts/framework/test_run.py | 4 ++--
dts/framework/test_suite.py | 2 +-
dts/framework/testbed_model/capability.py | 2 +-
dts/framework/testbed_model/linux_session.py | 2 +-
dts/framework/testbed_model/node.py | 2 +-
dts/framework/testbed_model/port.py | 2 +-
dts/framework/testbed_model/posix_session.py | 2 +-
dts/framework/testbed_model/topology.py | 2 +-
.../testbed_model/traffic_generator/__init__.py | 2 +-
dts/framework/testbed_model/traffic_generator/scapy.py | 2 +-
dts/framework/utils.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
| 2 +-
dts/tests/TestSuite_rte_flow.py | 10 +++++-----
34 files changed, 40 insertions(+), 41 deletions(-)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename dts/{framework => api}/exception.py (100%)
diff --git a/doc/api/dts/framework.exception.rst b/doc/api/dts/api.exception.rst
similarity index 77%
rename from doc/api/dts/framework.exception.rst
rename to doc/api/dts/api.exception.rst
index efb47dc5ae..8e6bff5ee7 100644
--- a/doc/api/dts/framework.exception.rst
+++ b/doc/api/dts/api.exception.rst
@@ -3,6 +3,6 @@
exception - Exceptions
======================
-.. automodule:: framework.exception
+.. automodule:: api.exception
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index c719297c11..01f630e7cd 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -36,7 +36,7 @@ Modules
framework.logger
framework.parser
framework.utils
- framework.exception
+ api.exception
Indices and tables
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 24a2b05063..7d04c7ab49 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -47,7 +47,7 @@
from paramiko import SFTPClient, SFTPFile
from typing_extensions import Buffer
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
from framework.testbed_model.node import Node, NodeIdentifier, get_node
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index a4fafc3713..c6a220dced 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -22,8 +22,8 @@
ThroughputResults,
VerifyResults,
)
+from api.exception import RemoteCommandExecutionError, SkippedTestException
from framework.context import get_ctx
-from framework.exception import RemoteCommandExecutionError, SkippedTestException
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/framework/exception.py b/dts/api/exception.py
similarity index 100%
rename from dts/framework/exception.py
rename to dts/api/exception.py
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 094a1b7a9d..cabb39a8dd 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,9 +27,9 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.exception import InternalError
from api.test import fail, log_debug
from framework.context import get_ctx
-from framework.exception import InternalError
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index e17babe0ca..9cad9a9495 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.context import get_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..9498d723d5 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
@@ -55,7 +56,6 @@
VLANOffloadFlag,
)
from framework.context import get_ctx
-from framework.exception import InteractiveCommandExecutionError, InternalError
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index d2f0138e4a..566dc7c4a2 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -35,7 +35,7 @@
from pydantic import Field, TypeAdapter, ValidationError, model_validator
from typing_extensions import Self
-from framework.exception import ConfigurationError
+from api.exception import ConfigurationError
from .common import FrozenModel, ValidationContext
from .node import NodeConfiguration
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 76e24d1785..62aaba033a 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -27,7 +27,7 @@
)
from typing_extensions import TYPE_CHECKING, Self
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 8f1021dc96..7e61c85998 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -8,7 +8,7 @@
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 3075c36857..ebf470ad30 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -15,7 +15,7 @@
from typing_extensions import Self
-from framework.exception import InternalError
+from api.exception import InternalError
class ParserFn(TypedDict):
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index c3575cfcaf..d803a9e4bd 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,6 +13,7 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.exception import ConfigurationError, RemoteFileNotFoundError
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -25,7 +26,6 @@
RemoteDPDKTreeLocation,
)
from framework.context import get_ctx
-from framework.exception import ConfigurationError, RemoteFileNotFoundError
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/interactive_remote_session.py b/dts/framework/remote_session/interactive_remote_session.py
index c8156b4345..04f45e0df8 100644
--- a/dts/framework/remote_session/interactive_remote_session.py
+++ b/dts/framework/remote_session/interactive_remote_session.py
@@ -15,8 +15,8 @@
SSHException,
)
+from api.exception import SSHConnectionError
from framework.config.node import NodeConfiguration
-from framework.exception import SSHConnectionError
from framework.logger import DTSLogger
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index a65cbce209..fdd074be3a 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,12 +29,12 @@
from paramiko import Channel, channel
from typing_extensions import Self
-from framework.context import get_ctx
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from framework.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py
index 158325bb7f..f49966070f 100644
--- a/dts/framework/remote_session/remote_session.py
+++ b/dts/framework/remote_session/remote_session.py
@@ -24,13 +24,13 @@
SSHException,
)
-from framework.config.node import NodeConfiguration
-from framework.exception import (
+from api.exception import (
RemoteCommandExecutionError,
SSHConnectionError,
SSHSessionDeadError,
SSHTimeoutError,
)
+from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.settings import SETTINGS
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index 6ea4749ff4..a0d8039a04 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -12,8 +12,8 @@
import sys
import textwrap
+from api.exception import ConfigurationError
from framework.config.common import ValidationContext
-from framework.exception import ConfigurationError
from framework.test_run import TestRun
from framework.testbed_model.node import Node
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 21faa55dc1..3cecb928ca 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -35,12 +35,11 @@
)
from typing_extensions import OrderedDict
+from api.exception import DTSError, ErrorSeverity, InternalError
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
from framework.testbed_model.os_session import OSSessionInfo
-from .exception import DTSError, ErrorSeverity, InternalError
-
class Result(IntEnum):
"""The possible states that a setup, a teardown or a test case may end up in."""
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..bbaf4f1fdf 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,9 +106,9 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
@@ -136,7 +136,7 @@ class TestRun:
If an error occurs, the current stage is aborted, the error is recorded, everything in
the inner stages is marked as blocked and the run continues in the next iteration
of the same stage. The return code is the highest `severity` of all
- :class:`~.framework.exception.DTSError`\s.
+ :class:`~.api.exception.DTSError`\s.
Example:
An error occurs in a test suite setup. The current test suite is aborted,
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 69ce26040a..e06fdd28b9 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -29,11 +29,11 @@
from scapy.packet import Packet
from typing_extensions import Self
+from api.exception import ConfigurationError, InternalError
from framework.config.common import FrozenModel
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
-from .exception import ConfigurationError, InternalError
from .logger import DTSLogger, get_dts_logger
from .utils import to_pascal_case
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 960370fc72..001b65994c 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -64,7 +64,7 @@ def test_scatter_mbuf_2048(self):
from typing_extensions import Self
from api.capabilities import LinkTopology, NicCapability
-from framework.exception import ConfigurationError, InternalError, SkippedTestException
+from api.exception import ConfigurationError, InternalError, SkippedTestException
from framework.logger import get_dts_logger
from framework.testbed_model.node import Node
from framework.testbed_model.port import DriverKind
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index ee943462c2..88b6da1ae6 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -18,7 +18,7 @@
from typing_extensions import NotRequired
-from framework.exception import (
+from api.exception import (
ConfigurationError,
InternalError,
RemoteCommandExecutionError,
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 67a96ef4e5..4f42bf6aeb 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -17,11 +17,11 @@
from pathlib import PurePath
from typing import Literal, TypeAlias
+from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
-from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
diff --git a/dts/framework/testbed_model/port.py b/dts/framework/testbed_model/port.py
index d81bc4cda0..aea3e59c25 100644
--- a/dts/framework/testbed_model/port.py
+++ b/dts/framework/testbed_model/port.py
@@ -12,8 +12,8 @@
from functools import cached_property
from typing import TYPE_CHECKING, Any, Final, Literal, NamedTuple
+from api.exception import InternalError
from framework.config.node import PortConfig
-from framework.exception import InternalError
if TYPE_CHECKING:
from .node import Node
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/framework/testbed_model/posix_session.py
index dec952685a..db2c3c0c40 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -16,7 +16,7 @@
from collections.abc import Iterable
from pathlib import Path, PurePath, PurePosixPath
-from framework.exception import DPDKBuildError, RemoteCommandExecutionError
+from api.exception import DPDKBuildError, RemoteCommandExecutionError
from framework.settings import SETTINGS
from framework.utils import (
MesonArgs,
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 34862c4d2e..805a762c19 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -18,7 +18,7 @@
from typing_extensions import Self
from api.capabilities import LinkTopology
-from framework.exception import ConfigurationError, InternalError
+from api.exception import ConfigurationError, InternalError
from framework.testbed_model.linux_session import LinuxSession
from framework.testbed_model.node import Node, NodeIdentifier
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/framework/testbed_model/traffic_generator/__init__.py
index fca251f534..324b5e88f3 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/framework/testbed_model/traffic_generator/__init__.py
@@ -14,12 +14,12 @@
and a capturing traffic generator is required.
"""
+from api.exception import ConfigurationError
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.exception import ConfigurationError
from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/framework/testbed_model/traffic_generator/scapy.py
index c6e9006205..e983443548 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/framework/testbed_model/traffic_generator/scapy.py
@@ -25,9 +25,9 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet
+from api.exception import InteractiveSSHSessionDeadError, InternalError
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
-from framework.exception import InteractiveSSHSessionDeadError, InternalError
from framework.remote_session.python_shell import PythonShell
from framework.testbed_model.node import Node
from framework.testbed_model.port import Port
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 9917ffbfaa..28e344871a 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -26,7 +26,7 @@
from scapy.layers.inet import IP, TCP, UDP, Ether
from scapy.packet import Packet
-from .exception import InternalError
+from api.exception import InternalError
REGEX_FOR_PCI_ADDRESS: str = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}"
_REGEX_FOR_COLON_OR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}[:-]){5}[\da-fA-F]{2}"
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index af0a5680ab..f36b48a153 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -29,9 +29,9 @@
from api.cryptodev.types import (
CryptodevResults,
)
+from api.exception import SkippedTestException
from api.test import verify
from framework.context import get_ctx
-from framework.exception import SkippedTestException
from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index 5ac85bee7d..b62efa2b42 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -35,11 +35,11 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import TestSuite, func_test
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index a7e24b37d5..b44822d31c 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -23,10 +23,10 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
from api.testpmd import TestPmd
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import TestSuite, func_test
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index f6adf262c3..1e5a6860be 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -20,6 +20,7 @@
requires_link_topology,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
from api.testpmd import TestPmd
@@ -29,7 +30,6 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import BaseConfig, TestSuite, func_test
from framework.utils import StrEnum
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 6255e4c36d..7e50a075ac 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -21,15 +21,15 @@
from scapy.packet import Packet, Raw
from api.capabilities import NicCapability, requires_nic_capability
-from api.packet import send_packet_and_capture
-from api.test import fail, log, verify
-from api.testpmd import TestPmd
-from api.testpmd.types import FlowRule
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
SkippedTestException,
TestCaseVerifyError,
)
+from api.packet import send_packet_and_capture
+from api.test import fail, log, verify
+from api.testpmd import TestPmd
+from api.testpmd.types import FlowRule
from framework.test_suite import TestSuite, func_test
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 2/7] dts: move utils from framework to API
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-28 18:08 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
@ 2026-04-28 18:08 ` Dean Marx
2026-04-28 18:08 ` [PATCH v4 3/7] dts: move context " Dean Marx
` (4 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-28 18:08 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The utils module is used to generate a set of random
packets in certain test suites. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/config.py | 2 +-
dts/api/packet.py | 2 +-
dts/api/testpmd/config.py | 2 +-
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 0
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/test_suite.py | 2 +-
dts/framework/testbed_model/cpu.py | 2 +-
dts/framework/testbed_model/linux_session.py | 2 +-
dts/framework/testbed_model/os_session.py | 2 +-
dts/framework/testbed_model/posix_session.py | 4 ++--
.../traffic_generator/capturing_traffic_generator.py | 2 +-
.../testbed_model/traffic_generator/traffic_generator.py | 2 +-
dts/framework/testbed_model/traffic_generator/trex.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
| 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
22 files changed, 22 insertions(+), 22 deletions(-)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
rename dts/{framework => api}/utils.py (100%)
diff --git a/doc/api/dts/framework.utils.rst b/doc/api/dts/api.utils.rst
similarity index 80%
rename from doc/api/dts/framework.utils.rst
rename to doc/api/dts/api.utils.rst
index cc06d4c3c3..0b4baff1b5 100644
--- a/doc/api/dts/framework.utils.rst
+++ b/doc/api/dts/api.utils.rst
@@ -3,6 +3,6 @@
utils - Various Utilities
=========================
-.. automodule:: framework.utils
+.. automodule:: api.utils
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 01f630e7cd..783270f6e9 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -35,7 +35,7 @@ Modules
framework.context
framework.logger
framework.parser
- framework.utils
+ api.utils
api.exception
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index 69ff7aa59a..a88e70d45c 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.utils import StrEnum
from framework.params import Params, Switch
from framework.params.eal import EalParams
-from framework.utils import StrEnum
Silent = Literal[""]
diff --git a/dts/api/packet.py b/dts/api/packet.py
index cabb39a8dd..b7a9bb28bf 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -29,6 +29,7 @@
from api.exception import InternalError
from api.test import fail, log_debug
+from api.utils import get_packet_summaries
from framework.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -36,7 +37,6 @@
from framework.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
-from framework.utils import get_packet_summaries
def send_packet_and_capture(
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index e71a3e1ef0..8b688834ee 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,6 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
+from api.utils import StrEnum
from framework.params import (
Params,
Switch,
@@ -24,7 +25,6 @@
str_from_flag_value,
)
from framework.params.eal import EalParams
-from framework.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..5c847b4bd6 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -15,8 +15,8 @@
from typing_extensions import Self
+from api.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
from framework.parser import ParserFn, TextParser
-from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
RxTxLiteralSwitch = Literal["rx", "tx"]
diff --git a/dts/framework/utils.py b/dts/api/utils.py
similarity index 100%
rename from dts/framework/utils.py
rename to dts/api/utils.py
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 792290f11f..28f23389a7 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -14,7 +14,7 @@
from pydantic import Field, model_validator
from typing_extensions import Self
-from framework.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
+from api.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
from .common import FrozenModel
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 62aaba033a..977067f42a 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -28,7 +28,7 @@
from typing_extensions import TYPE_CHECKING, Self
from api.exception import InternalError
-from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
+from api.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index d803a9e4bd..69b47b823b 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -14,6 +14,7 @@
from typing import ClassVar, Final
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -33,7 +34,6 @@
from framework.testbed_model.node import Node
from framework.testbed_model.os_session import OSSession
from framework.testbed_model.virtual_device import VirtualDevice
-from framework.utils import MesonArgs, TarCompressionFormat
@dataclass(slots=True, frozen=True)
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index e06fdd28b9..426c98fdf6 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -30,12 +30,12 @@
from typing_extensions import Self
from api.exception import ConfigurationError, InternalError
+from api.utils import to_pascal_case
from framework.config.common import FrozenModel
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
from .logger import DTSLogger, get_dts_logger
-from .utils import to_pascal_case
if TYPE_CHECKING:
from framework.context import Context
diff --git a/dts/framework/testbed_model/cpu.py b/dts/framework/testbed_model/cpu.py
index 6e2ecca080..52ef196f84 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/framework/testbed_model/cpu.py
@@ -24,7 +24,7 @@
from dataclasses import dataclass
from enum import auto, unique
-from framework.utils import StrEnum, expand_range
+from api.utils import StrEnum, expand_range
@unique
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 88b6da1ae6..69b0923744 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -23,8 +23,8 @@
InternalError,
RemoteCommandExecutionError,
)
+from api.utils import expand_range
from framework.testbed_model.port import PortInfo
-from framework.utils import expand_range
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index 2c267afed1..7bb339fab2 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -29,12 +29,12 @@
from enum import Flag, auto
from pathlib import Path, PurePath, PurePosixPath
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.remote_session.interactive_remote_session import InteractiveRemoteSession
from framework.remote_session.remote_session import CommandResult, RemoteSession
from framework.settings import SETTINGS
-from framework.utils import MesonArgs, TarCompressionFormat
from .cpu import Architecture, LogicalCore
from .port import Port, PortInfo
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/framework/testbed_model/posix_session.py
index db2c3c0c40..61c634dad1 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -17,14 +17,14 @@
from pathlib import Path, PurePath, PurePosixPath
from api.exception import DPDKBuildError, RemoteCommandExecutionError
-from framework.settings import SETTINGS
-from framework.utils import (
+from api.utils import (
MesonArgs,
TarCompressionFormat,
convert_to_list_of_string,
create_tarball,
extract_tarball,
)
+from framework.settings import SETTINGS
from .cpu import Architecture
from .os_session import FilePermissions, OSSession, OSSessionInfo
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
index 7655751d7e..2804d64990 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
+from api.utils import get_packet_summaries
from framework.testbed_model.port import Port
-from framework.utils import get_packet_summaries
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/framework/testbed_model/traffic_generator/traffic_generator.py
index cdda5a7c08..fedce77fdf 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/framework/testbed_model/traffic_generator/traffic_generator.py
@@ -25,7 +25,7 @@ class TrafficGenerator(ABC):
Exposes the common public methods of all traffic generators and defines private methods
that must implement the traffic generation logic in subclasses. This class also extends from
- :class:`framework.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
+ :class:`api.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
from multiple classes to fulfil the traffic generating functionality without breaking
single inheritance.
"""
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/framework/testbed_model/traffic_generator/trex.py
index 22cd20dea9..2064703fcc 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/framework/testbed_model/traffic_generator/trex.py
@@ -11,6 +11,7 @@
from scapy.packet import Packet
+from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
@@ -23,7 +24,6 @@
PerformanceTrafficGenerator,
PerformanceTrafficStats,
)
-from framework.utils import StrEnum
@dataclass(slots=True)
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 596b892730..1e99b82b8c 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -20,10 +20,10 @@
)
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
+from api.utils import generate_random_packets
from framework.context import filter_cores
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 1e5a6860be..4df273e3e1 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -30,8 +30,8 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
+from api.utils import StrEnum
from framework.test_suite import BaseConfig, TestSuite, func_test
-from framework.utils import StrEnum
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 271ad4301c..fce83604a6 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -20,11 +20,11 @@
)
from api.test import verify
from api.testpmd import TestPmd
+from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.linux_session import LinuxSession
-from framework.utils import REGEX_FOR_PCI_ADDRESS
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index fa91f7ee2f..c57a12c932 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -20,9 +20,9 @@
)
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
+from api.utils import generate_random_packets
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.virtual_device import VirtualDevice
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 3/7] dts: move context from framework to API
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-28 18:08 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
2026-04-28 18:08 ` [PATCH v4 2/7] dts: move utils " Dean Marx
@ 2026-04-28 18:08 ` Dean Marx
2026-04-28 18:08 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
` (3 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-28 18:08 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
A couple test suites import and get the run context
during execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.context.rst => api.context.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/{framework => api}/context.py | 0
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 4 ++--
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/framework/test_run.py | 2 +-
dts/framework/test_suite.py | 4 ++--
dts/framework/testbed_model/node.py | 2 +-
dts/framework/testbed_model/topology.py | 14 +++++++-------
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
18 files changed, 25 insertions(+), 25 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename dts/{framework => api}/context.py (100%)
diff --git a/doc/api/dts/framework.context.rst b/doc/api/dts/api.context.rst
similarity index 80%
rename from doc/api/dts/framework.context.rst
rename to doc/api/dts/api.context.rst
index 925c160360..65b4ab9a47 100644
--- a/doc/api/dts/framework.context.rst
+++ b/doc/api/dts/api.context.rst
@@ -3,6 +3,6 @@
context - DTS execution context
===============================
-.. automodule:: framework.context
+.. automodule:: api.context
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 783270f6e9..98269d6e80 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -32,7 +32,7 @@ Modules
framework.test_suite
framework.test_result
framework.settings
- framework.context
+ api.context
framework.logger
framework.parser
api.utils
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 7d04c7ab49..f3dd07de56 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -86,7 +86,7 @@ def make_file_path(
path /= custom_path
else:
- from framework.context import get_ctx
+ from api.context import get_ctx
try:
ctx = get_ctx()
diff --git a/dts/framework/context.py b/dts/api/context.py
similarity index 100%
rename from dts/framework/context.py
rename to dts/api/context.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index c6a220dced..67dcb02130 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -23,7 +23,7 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index b7a9bb28bf..59f26da833 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -30,7 +30,7 @@
from api.exception import InternalError
from api.test import fail, log_debug
from api.utils import get_packet_summaries
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
@@ -82,7 +82,7 @@ def send_packets_and_capture(
Returns:
A list of received packets.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index 9cad9a9495..a1f2326075 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -11,7 +11,7 @@
from api.artifact import Artifact
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9498d723d5..a528663c21 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -55,7 +55,7 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index c3b02dcc62..07db6dfeb0 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -30,7 +30,7 @@
from typing_extensions import Self
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 69b47b823b..9f7cd60dfe 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -26,7 +26,7 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 269c2cada4..a8f169787c 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -10,7 +10,7 @@
from abc import ABC, abstractmethod
from pathlib import PurePath
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index fdd074be3a..d138727c85 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -34,7 +34,7 @@
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index bbaf4f1fdf..605a916ebb 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -108,7 +108,7 @@
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.config.test_run import TestRunConfiguration
-from framework.context import Context, init_ctx
+from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 426c98fdf6..6066f7a77a 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -38,7 +38,7 @@
from .logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
- from framework.context import Context
+ from api.context import Context
class BaseConfig(FrozenModel):
@@ -92,7 +92,7 @@ def __init__(self, config: BaseConfig) -> None:
Args:
config: The test suite configuration.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self.config = config
self._ctx = get_ctx()
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 4f42bf6aeb..40dd7f0666 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -242,7 +242,7 @@ def get_node(node_identifier: NodeIdentifier) -> Node | None:
if node_identifier == "local":
return None
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
if node_identifier == "sut":
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 805a762c19..5b6ff2add5 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -96,7 +96,7 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
Raises:
InternalError: If the given `node_identifier` is invalid.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
match node_identifier:
@@ -180,7 +180,7 @@ def instantiate_crypto_ports(self) -> None:
Raises:
InternalError: If crypto virtual functions could not be created on a port.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
for port in ctx.sut_node.cryptodevs:
@@ -206,7 +206,7 @@ def instantiate_vf_ports(self) -> None:
Raises:
InternalError: If virtual function creation fails.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -235,7 +235,7 @@ def instantiate_vf_ports(self) -> None:
def delete_vf_ports(self) -> None:
"""Delete virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -246,7 +246,7 @@ def delete_vf_ports(self) -> None:
def delete_crypto_vf_ports(self) -> None:
"""Delete crypto virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -259,7 +259,7 @@ def bind_cryptodevs(self, driver: DriverKind):
Args:
driver: The driver to bind the crypto functions
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
@@ -318,7 +318,7 @@ def _prepare_devbind_script(self) -> None:
Raises:
InternalError: If dpdk-devbind.py could not be found.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
local_script_path = Path("..", "usertools", "dpdk-devbind.py").resolve()
valid_script_path = local_script_path.exists()
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index f36b48a153..39784cbcac 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -31,7 +31,7 @@
)
from api.exception import SkippedTestException
from api.test import verify
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 1e99b82b8c..c018efb1f7 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -21,7 +21,7 @@
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.context import filter_cores
+from api.context import filter_cores
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 4/7] dts: move testbed model from framework to API
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (2 preceding siblings ...)
2026-04-28 18:08 ` [PATCH v4 3/7] dts: move context " Dean Marx
@ 2026-04-28 18:08 ` Dean Marx
2026-04-28 18:08 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
` (2 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-28 18:08 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites import modules from testbed model
in the framework. Move this directory to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 +++++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
doc/api/dts/framework.testbed_model.rst | 28 -------------------
...mework.testbed_model.traffic_generator.rst | 16 -----------
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/capabilities.py | 8 +++---
dts/api/context.py | 10 +++----
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 12 ++++----
dts/api/test.py | 2 +-
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 10 +++----
dts/{framework => api}/testbed_model/cpu.py | 2 +-
.../testbed_model/linux_session.py | 2 +-
dts/{framework => api}/testbed_model/node.py | 0
.../testbed_model/os_session.py | 6 ++--
dts/{framework => api}/testbed_model/port.py | 0
.../testbed_model/posix_session.py | 0
.../testbed_model/topology.py | 4 +--
.../traffic_generator/__init__.py | 2 +-
.../capturing_traffic_generator.py | 2 +-
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 12 ++++----
.../traffic_generator/traffic_generator.py | 6 ++--
.../testbed_model/traffic_generator/trex.py | 14 +++++-----
.../testbed_model/virtual_device.py | 0
dts/api/testpmd/__init__.py | 2 +-
dts/framework/params/eal.py | 6 ++--
dts/framework/params/types.py | 6 ++--
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 10 +++----
dts/framework/remote_session/dpdk_shell.py | 2 +-
.../remote_session/interactive_shell.py | 4 +--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 2 +-
dts/framework/test_run.py | 18 ++++++------
dts/framework/test_suite.py | 4 +--
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 4 +--
dts/tests/TestSuite_l2fwd.py | 4 +--
dts/tests/TestSuite_packet_capture.py | 8 +++---
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 6 ++--
57 files changed, 148 insertions(+), 148 deletions(-)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (99%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
rename dts/{framework => api}/testbed_model/linux_session.py (99%)
rename dts/{framework => api}/testbed_model/node.py (100%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (100%)
rename dts/{framework => api}/testbed_model/posix_session.py (100%)
rename dts/{framework => api}/testbed_model/topology.py (99%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (97%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (99%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (94%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (97%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
diff --git a/doc/api/dts/framework.testbed_model.capability.rst b/doc/api/dts/api.testbed_model.capability.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.capability.rst
rename to doc/api/dts/api.testbed_model.capability.rst
index fab91cad83..88e396dddb 100644
--- a/doc/api/dts/framework.testbed_model.capability.rst
+++ b/doc/api/dts/api.testbed_model.capability.rst
@@ -3,6 +3,6 @@
capability - Testbed Capabilities
=================================
-.. automodule:: framework.testbed_model.capability
+.. automodule:: api.testbed_model.capability
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.cpu.rst b/doc/api/dts/api.testbed_model.cpu.rst
similarity index 78%
rename from doc/api/dts/framework.testbed_model.cpu.rst
rename to doc/api/dts/api.testbed_model.cpu.rst
index 997f2a9795..dbbb29480a 100644
--- a/doc/api/dts/framework.testbed_model.cpu.rst
+++ b/doc/api/dts/api.testbed_model.cpu.rst
@@ -3,6 +3,6 @@
cpu - CPU Representation and Utilities
======================================
-.. automodule:: framework.testbed_model.cpu
+.. automodule:: api.testbed_model.cpu
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.linux_session.rst b/doc/api/dts/api.testbed_model.linux_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.linux_session.rst
rename to doc/api/dts/api.testbed_model.linux_session.rst
index 7567816199..cfe79d8bca 100644
--- a/doc/api/dts/framework.testbed_model.linux_session.rst
+++ b/doc/api/dts/api.testbed_model.linux_session.rst
@@ -3,6 +3,6 @@
linux\_session - Linux Remote Session
=====================================
-.. automodule:: framework.testbed_model.linux_session
+.. automodule:: api.testbed_model.linux_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.node.rst b/doc/api/dts/api.testbed_model.node.rst
similarity index 71%
rename from doc/api/dts/framework.testbed_model.node.rst
rename to doc/api/dts/api.testbed_model.node.rst
index 23c6c46a00..15f522e5f7 100644
--- a/doc/api/dts/framework.testbed_model.node.rst
+++ b/doc/api/dts/api.testbed_model.node.rst
@@ -3,6 +3,6 @@
node - Base Node
================
-.. automodule:: framework.testbed_model.node
+.. automodule:: api.testbed_model.node
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.os_session.rst b/doc/api/dts/api.testbed_model.os_session.rst
similarity index 76%
rename from doc/api/dts/framework.testbed_model.os_session.rst
rename to doc/api/dts/api.testbed_model.os_session.rst
index ecfb352311..e7e3f9894f 100644
--- a/doc/api/dts/framework.testbed_model.os_session.rst
+++ b/doc/api/dts/api.testbed_model.os_session.rst
@@ -3,6 +3,6 @@
os\_session - OS-aware Remote Session ABC
=========================================
-.. automodule:: framework.testbed_model.os_session
+.. automodule:: api.testbed_model.os_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.port.rst b/doc/api/dts/api.testbed_model.port.rst
similarity index 77%
rename from doc/api/dts/framework.testbed_model.port.rst
rename to doc/api/dts/api.testbed_model.port.rst
index fdb7ca8a1d..d64501aef0 100644
--- a/doc/api/dts/framework.testbed_model.port.rst
+++ b/doc/api/dts/api.testbed_model.port.rst
@@ -3,7 +3,7 @@
port - NIC Port Representation
==============================
-.. automodule:: framework.testbed_model.port
+.. automodule:: api.testbed_model.port
:members:
:show-inheritance:
:noindex:
diff --git a/doc/api/dts/framework.testbed_model.posix_session.rst b/doc/api/dts/api.testbed_model.posix_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.posix_session.rst
rename to doc/api/dts/api.testbed_model.posix_session.rst
index e65585fd85..9f0e9ff18d 100644
--- a/doc/api/dts/framework.testbed_model.posix_session.rst
+++ b/doc/api/dts/api.testbed_model.posix_session.rst
@@ -3,6 +3,6 @@
posix\_session - Posix Remote Session
=====================================
-.. automodule:: framework.testbed_model.posix_session
+.. automodule:: api.testbed_model.posix_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.rst b/doc/api/dts/api.testbed_model.rst
new file mode 100644
index 0000000000..5e2e5189b2
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.rst
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+testbed\_model - Testbed Modelling Package
+==========================================
+
+.. automodule:: api.testbed_model
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 2
+
+ api.testbed_model.traffic_generator
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.os_session
+ api.testbed_model.linux_session
+ api.testbed_model.posix_session
+ api.testbed_model.node
+ api.testbed_model.capability
+ api.testbed_model.cpu
+ api.testbed_model.port
+ api.testbed_model.topology
+ api.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.topology.rst b/doc/api/dts/api.testbed_model.topology.rst
similarity index 73%
rename from doc/api/dts/framework.testbed_model.topology.rst
rename to doc/api/dts/api.testbed_model.topology.rst
index 496f2a895f..bb63fe38dc 100644
--- a/doc/api/dts/framework.testbed_model.topology.rst
+++ b/doc/api/dts/api.testbed_model.topology.rst
@@ -3,6 +3,6 @@
topology - Testbed Topology
===========================
-.. automodule:: framework.testbed_model.topology
+.. automodule:: api.testbed_model.topology
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
similarity index 68%
rename from doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
index 29fa834042..cfe03201a7 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
@@ -3,6 +3,6 @@
capturing\_traffic\_generator - Base Capturing TG ABC
=====================================================
-.. automodule:: framework.testbed_model.traffic_generator.capturing_traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.capturing_traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.rst
new file mode 100644
index 0000000000..311bdcf6b9
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.traffic_generator.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+traffic\_generator Subpackage
+=============================
+
+.. automodule:: api.testbed_model.traffic_generator
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.traffic_generator.traffic_generator
+ api.testbed_model.traffic_generator.capturing_traffic_generator
+ api.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
similarity index 70%
rename from doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
index df78ac9514..949bb66632 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
@@ -3,6 +3,6 @@
scapy - Capturing Traffic Generator
===================================
-.. automodule:: framework.testbed_model.traffic_generator.scapy
+.. automodule:: api.testbed_model.traffic_generator.scapy
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
similarity index 65%
rename from doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
index bfec728dee..1045e534b5 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
@@ -3,6 +3,6 @@
traffic\_generator - Base TG ABC
================================
-.. automodule:: framework.testbed_model.traffic_generator.traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.virtual_device.rst b/doc/api/dts/api.testbed_model.virtual_device.rst
similarity index 72%
rename from doc/api/dts/framework.testbed_model.virtual_device.rst
rename to doc/api/dts/api.testbed_model.virtual_device.rst
index a6b0420e75..97adc895f6 100644
--- a/doc/api/dts/framework.testbed_model.virtual_device.rst
+++ b/doc/api/dts/api.testbed_model.virtual_device.rst
@@ -3,6 +3,6 @@
virtual\_device - Virtual Devices
=================================
-.. automodule:: framework.testbed_model.virtual_device
+.. automodule:: api.testbed_model.virtual_device
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.rst b/doc/api/dts/framework.testbed_model.rst
deleted file mode 100644
index f283178f6a..0000000000
--- a/doc/api/dts/framework.testbed_model.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-testbed\_model - Testbed Modelling Package
-==========================================
-
-.. automodule:: framework.testbed_model
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 2
-
- framework.testbed_model.traffic_generator
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.os_session
- framework.testbed_model.linux_session
- framework.testbed_model.posix_session
- framework.testbed_model.node
- framework.testbed_model.capability
- framework.testbed_model.cpu
- framework.testbed_model.port
- framework.testbed_model.topology
- framework.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.rst b/doc/api/dts/framework.testbed_model.traffic_generator.rst
deleted file mode 100644
index 24c250ee3a..0000000000
--- a/doc/api/dts/framework.testbed_model.traffic_generator.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-traffic\_generator Subpackage
-=============================
-
-.. automodule:: framework.testbed_model.traffic_generator
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.traffic_generator.traffic_generator
- framework.testbed_model.traffic_generator.capturing_traffic_generator
- framework.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 98269d6e80..f47e4af3f2 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -16,7 +16,7 @@ Packages
tests
api
- framework.testbed_model
+ api.testbed_model
framework.remote_session
framework.params
framework.config
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index f3dd07de56..74a8ac667f 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -48,9 +48,9 @@
from typing_extensions import Buffer
from api.exception import InternalError
+from api.testbed_model.node import Node, NodeIdentifier, get_node
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node, NodeIdentifier, get_node
TextMode: TypeAlias = (
Literal["r", "r+", "w", "w+", "a", "a+", "x", "x+"]
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 09bc538523..04fc20738b 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -23,7 +23,7 @@
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires_link_topology
+ from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -34,7 +34,7 @@ def hello_world_single_core(self):
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires_nic_capability
+ from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -235,7 +235,7 @@ def requires_link_topology(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import TopologyCapability
+ from api.testbed_model.capability import TopologyCapability
def add_required_topology(
test_case_or_suite: type["TestProtocol"],
@@ -258,7 +258,7 @@ def requires_nic_capability(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import DecoratedNicCapability
+ from api.testbed_model.capability import DecoratedNicCapability
def add_required_capability(
test_case_or_suite: type["TestProtocol"],
diff --git a/dts/api/context.py b/dts/api/context.py
index 7e61c85998..13a2ad6c39 100644
--- a/dts/api/context.py
+++ b/dts/api/context.py
@@ -9,17 +9,17 @@
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
from api.exception import InternalError
+from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
+from api.testbed_model.node import Node
+from api.testbed_model.topology import Topology
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
-from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
+ from api.testbed_model.capability import TestProtocol
+ from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.test_suite import TestCase, TestSuite
- from framework.testbed_model.capability import TestProtocol
- from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
P = ParamSpec("P")
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 67dcb02130..1ba8e0d977 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -14,6 +14,7 @@
from typing_extensions import Unpack
+from api.context import get_ctx
from api.cryptodev.config import CryptoPmdParams, TestType
from api.cryptodev.types import (
CryptodevResults,
@@ -23,7 +24,6 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 59f26da833..bf90961c26 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,16 +27,16 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.context import get_ctx
from api.exception import InternalError
from api.test import fail, log_debug
-from api.utils import get_packet_summaries
-from api.context import get_ctx
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
+from api.utils import get_packet_summaries
def send_packet_and_capture(
@@ -83,7 +83,7 @@ def send_packets_and_capture(
A list of received packets.
"""
from api.context import get_ctx
- from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+ from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
@@ -340,7 +340,7 @@ def assess_performance_by_packet(
Returns:
Performance statistics of the generated test.
"""
- from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+ from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficGenerator,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index a1f2326075..03846639ad 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
-from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.context import get_ctx
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/framework/testbed_model/__init__.py b/dts/api/testbed_model/__init__.py
similarity index 100%
rename from dts/framework/testbed_model/__init__.py
rename to dts/api/testbed_model/__init__.py
diff --git a/dts/framework/testbed_model/capability.py b/dts/api/testbed_model/capability.py
similarity index 99%
rename from dts/framework/testbed_model/capability.py
rename to dts/api/testbed_model/capability.py
index 001b65994c..4e4e976be5 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -26,7 +26,7 @@
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires
+ from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -37,7 +37,7 @@ def hello_world_single_core(self):
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires
+ from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -65,10 +65,10 @@ def test_scatter_mbuf_2048(self):
from api.capabilities import LinkTopology, NicCapability
from api.exception import ConfigurationError, InternalError, SkippedTestException
+from api.testbed_model.node import Node
+from api.testbed_model.port import DriverKind
+from api.testbed_model.topology import Topology
from framework.logger import get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import DriverKind
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
from api.testpmd import TestPmd
diff --git a/dts/framework/testbed_model/cpu.py b/dts/api/testbed_model/cpu.py
similarity index 99%
rename from dts/framework/testbed_model/cpu.py
rename to dts/api/testbed_model/cpu.py
index 52ef196f84..ee754f5844 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/api/testbed_model/cpu.py
@@ -29,7 +29,7 @@
@unique
class Architecture(StrEnum):
- r"""The supported architectures of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported architectures of :class:`~api.testbed_model.node.Node`\s."""
#:
i686 = auto()
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
similarity index 99%
rename from dts/framework/testbed_model/linux_session.py
rename to dts/api/testbed_model/linux_session.py
index 69b0923744..7307b2abe2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -23,8 +23,8 @@
InternalError,
RemoteCommandExecutionError,
)
+from api.testbed_model.port import PortInfo
from api.utils import expand_range
-from framework.testbed_model.port import PortInfo
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/node.py b/dts/api/testbed_model/node.py
similarity index 100%
rename from dts/framework/testbed_model/node.py
rename to dts/api/testbed_model/node.py
diff --git a/dts/framework/testbed_model/os_session.py b/dts/api/testbed_model/os_session.py
similarity index 99%
rename from dts/framework/testbed_model/os_session.py
rename to dts/api/testbed_model/os_session.py
index 7bb339fab2..b1e0538ac9 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/api/testbed_model/os_session.py
@@ -73,11 +73,11 @@ class OSSessionInfo:
Attributes:
os_name: The name of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
os_version: The version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
kernel_version: The kernel version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
"""
os_name: str
diff --git a/dts/framework/testbed_model/port.py b/dts/api/testbed_model/port.py
similarity index 100%
rename from dts/framework/testbed_model/port.py
rename to dts/api/testbed_model/port.py
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/api/testbed_model/posix_session.py
similarity index 100%
rename from dts/framework/testbed_model/posix_session.py
rename to dts/api/testbed_model/posix_session.py
diff --git a/dts/framework/testbed_model/topology.py b/dts/api/testbed_model/topology.py
similarity index 99%
rename from dts/framework/testbed_model/topology.py
rename to dts/api/testbed_model/topology.py
index 5b6ff2add5..11593d64d5 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/api/testbed_model/topology.py
@@ -19,8 +19,8 @@
from api.capabilities import LinkTopology
from api.exception import ConfigurationError, InternalError
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.node import Node, NodeIdentifier
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.node import Node, NodeIdentifier
from .port import DriverKind, Port, PortConfig
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/api/testbed_model/traffic_generator/__init__.py
similarity index 97%
rename from dts/framework/testbed_model/traffic_generator/__init__.py
rename to dts/api/testbed_model/traffic_generator/__init__.py
index 324b5e88f3..11fa25448a 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/api/testbed_model/traffic_generator/__init__.py
@@ -15,12 +15,12 @@
"""
from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
similarity index 99%
rename from dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
index 2804d64990..db274e5e82 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
+from api.testbed_model.port import Port
from api.utils import get_packet_summaries
-from framework.testbed_model.port import Port
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py b/dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
similarity index 100%
rename from dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/api/testbed_model/traffic_generator/scapy.py
similarity index 98%
rename from dts/framework/testbed_model/traffic_generator/scapy.py
rename to dts/api/testbed_model/traffic_generator/scapy.py
index e983443548..215c57f93d 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/api/testbed_model/traffic_generator/scapy.py
@@ -26,15 +26,15 @@
from scapy.packet import Packet
from api.exception import InteractiveSSHSessionDeadError, InternalError
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
from .capturing_traffic_generator import CapturingTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/api/testbed_model/traffic_generator/traffic_generator.py
similarity index 94%
rename from dts/framework/testbed_model/traffic_generator/traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/traffic_generator.py
index fedce77fdf..5fd68e5144 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/traffic_generator.py
@@ -13,11 +13,11 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
from framework.config.test_run import TrafficGeneratorConfig
from framework.logger import DTSLogger, get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
class TrafficGenerator(ABC):
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/api/testbed_model/traffic_generator/trex.py
similarity index 97%
rename from dts/framework/testbed_model/traffic_generator/trex.py
rename to dts/api/testbed_model/traffic_generator/trex.py
index 2064703fcc..d97ed934c9 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/api/testbed_model/traffic_generator/trex.py
@@ -11,19 +11,19 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node, create_session
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
+ PerformanceTrafficGenerator,
+ PerformanceTrafficStats,
+)
from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node, create_session
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
- PerformanceTrafficGenerator,
- PerformanceTrafficStats,
-)
@dataclass(slots=True)
diff --git a/dts/framework/testbed_model/virtual_device.py b/dts/api/testbed_model/virtual_device.py
similarity index 100%
rename from dts/framework/testbed_model/virtual_device.py
rename to dts/api/testbed_model/virtual_device.py
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index a528663c21..9f47a15433 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.context import get_ctx
from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
@@ -55,7 +56,6 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/params/eal.py b/dts/framework/params/eal.py
index e84a20f02f..86bfd3fcc6 100644
--- a/dts/framework/params/eal.py
+++ b/dts/framework/params/eal.py
@@ -6,12 +6,12 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.virtual_device import VirtualDevice
from framework.params import Params, Switch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.virtual_device import VirtualDevice
if TYPE_CHECKING:
- from framework.testbed_model.port import Port
+ from api.testbed_model.port import Port
def _port_to_pci(port: "Port") -> str:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 3c7650474c..f2fa69f8b8 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -32,6 +32,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.port import Port
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -54,9 +57,6 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TxUDPPortPair,
)
from framework.params import Switch, YesNoSwitch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.port import Port
-from framework.testbed_model.virtual_device import VirtualDevice
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 07db6dfeb0..84db3974b1 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,11 +31,11 @@
from typing_extensions import Self
from api.context import get_ctx
+from api.testbed_model.node import Node
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
-from framework.testbed_model.node import Node
P = TypeVar("P", bound=Params)
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 9f7cd60dfe..713a564d25 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,7 +13,12 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
+from api.testbed_model.node import Node
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
@@ -26,14 +31,9 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
-from framework.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
-from framework.testbed_model.node import Node
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.virtual_device import VirtualDevice
@dataclass(slots=True, frozen=True)
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index a8f169787c..b807f9bdae 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,12 +11,12 @@
from pathlib import PurePath
from api.context import get_ctx
+from api.testbed_model.cpu import LogicalCoreList
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
)
-from framework.testbed_model.cpu import LogicalCoreList
def compute_eal_params(
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index d138727c85..ec539bad95 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,16 +29,16 @@
from paramiko import Channel, channel
from typing_extensions import Self
+from api.context import get_ctx
from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
-from api.context import get_ctx
+from api.testbed_model.node import Node
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node
P = ParamSpec("P")
T = TypeVar("T", bound="InteractiveShell")
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index a0d8039a04..29be7b80fe 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -13,9 +13,9 @@
import textwrap
from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.common import ValidationContext
from framework.test_run import TestRun
-from framework.testbed_model.node import Node
from .config import Configuration, load_config
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 3cecb928ca..5f945163ce 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -36,9 +36,9 @@
from typing_extensions import OrderedDict
from api.exception import DTSError, ErrorSeverity, InternalError
+from api.testbed_model.os_session import OSSessionInfo
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
-from framework.testbed_model.os_session import OSSessionInfo
class Result(IntEnum):
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 605a916ebb..790fbf997d 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,22 +106,22 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
+from api.testbed_model.capability import (
+ Capability,
+ get_supported_capabilities,
+ test_if_supported,
+)
+from api.testbed_model.node import Node
+from api.testbed_model.topology import PortLink, Topology
+from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
-from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
-from framework.testbed_model.capability import (
- Capability,
- get_supported_capabilities,
- test_if_supported,
-)
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import PortLink, Topology
-from framework.testbed_model.traffic_generator import create_traffic_generator
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 6066f7a77a..786cfc7bff 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -30,10 +30,10 @@
from typing_extensions import Self
from api.exception import ConfigurationError, InternalError
+from api.testbed_model.capability import TestProtocol
+from api.testbed_model.topology import Topology
from api.utils import to_pascal_case
from framework.config.common import FrozenModel
-from framework.testbed_model.capability import TestProtocol
-from framework.testbed_model.topology import Topology
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index c57231de22..97e03b8fb7 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,9 +11,9 @@
requires_link_topology,
)
from api.test import verify
+from api.testbed_model.port import Port
from api.testpmd import TestPmd
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.port import Port
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 39784cbcac..fc4b3cb308 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -13,6 +13,7 @@
LinkTopology,
requires_link_topology,
)
+from api.context import get_ctx
from api.cryptodev import Cryptodev
from api.cryptodev.config import (
AeadAlgName,
@@ -31,9 +32,8 @@
)
from api.exception import SkippedTestException
from api.test import verify
-from api.context import get_ctx
+from api.testbed_model.virtual_device import VirtualDevice
from framework.test_suite import BaseConfig, TestSuite, crypto_test
-from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index c018efb1f7..5650366c36 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -13,17 +13,17 @@
requires_link_topology,
requires_nic_capability,
)
+from api.context import filter_cores
from api.packet import (
get_expected_packets,
match_all_packets,
send_packets_and_capture,
)
+from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from api.context import filter_cores
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.cpu import LogicalCoreCount
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 4bd15e2401..042b7019aa 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,15 +36,15 @@
send_packets_and_capture,
)
from api.test import verify
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from api.testpmd import TestPmd
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index fce83604a6..656e2e4bb7 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,12 +19,12 @@
requires_link_topology,
)
from api.test import verify
+from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.linux_session import LinuxSession
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index c57a12c932..91a6d4eb9f 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,11 +18,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
from api.utils import generate_random_packets
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.virtual_device import VirtualDevice
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index bdecdb76fd..c649aac197 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,12 +12,12 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.testbed_model.capability import requires
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.capability import requires
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.virtual_device import VirtualDevice
class TestVirtioFwd(TestSuite):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 5/7] dts: move test suite module from framework to API
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (3 preceding siblings ...)
2026-04-28 18:08 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
@ 2026-04-28 18:08 ` Dean Marx
2026-04-28 18:08 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
2026-04-28 18:08 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-28 18:08 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Currently, each test suite imports the TestSuite class
from the DTS framework to use as a base class.
However, the goal for 26.07 is to move all test suite
imports to the API module. Moves and updates the test_suite
file to the API directory, and updates all files that import
test_suite to reflect this change.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.test_suite.rst => api.test_suite.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/capabilities.py | 6 +++---
dts/api/context.py | 2 +-
dts/api/packet.py | 2 +-
dts/{framework => api}/test_suite.py | 3 +--
dts/api/testbed_model/capability.py | 6 +++---
dts/framework/config/__init__.py | 4 ++--
dts/framework/config/test_run.py | 8 ++++----
dts/framework/test_run.py | 2 +-
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
| 2 +-
dts/tests/TestSuite_port_control.py | 2 +-
dts/tests/TestSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 2 +-
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 2 +-
dts/tests/TestSuite_vlan.py | 2 +-
37 files changed, 45 insertions(+), 46 deletions(-)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename dts/{framework => api}/test_suite.py (99%)
diff --git a/doc/api/dts/framework.test_suite.rst b/doc/api/dts/api.test_suite.rst
similarity index 81%
rename from doc/api/dts/framework.test_suite.rst
rename to doc/api/dts/api.test_suite.rst
index 9517f51a4a..4acb7b103a 100644
--- a/doc/api/dts/framework.test_suite.rst
+++ b/doc/api/dts/api.test_suite.rst
@@ -3,6 +3,6 @@
test\_suite - Common Test Suite Features
========================================
-.. automodule:: framework.test_suite
+.. automodule:: api.test_suite
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index f47e4af3f2..7c282bbba1 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -29,7 +29,7 @@ Modules
framework.runner
framework.test_run
- framework.test_suite
+ api.test_suite
framework.test_result
framework.settings
api.context
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 04fc20738b..a4d6b2b424 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -22,7 +22,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -33,7 +33,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -47,7 +47,7 @@ def test_scatter_mbuf_2048(self):
from typing import TYPE_CHECKING, Callable
if TYPE_CHECKING:
- from framework.test_suite import TestProtocol
+ from api.test_suite import TestProtocol
class LinkTopology(IntEnum):
diff --git a/dts/api/context.py b/dts/api/context.py
index 13a2ad6c39..7ed4cc5665 100644
--- a/dts/api/context.py
+++ b/dts/api/context.py
@@ -16,10 +16,10 @@
from framework.settings import SETTINGS
if TYPE_CHECKING:
+ from api.test_suite import TestCase, TestSuite
from api.testbed_model.capability import TestProtocol
from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
- from framework.test_suite import TestCase, TestSuite
P = ParamSpec("P")
diff --git a/dts/api/packet.py b/dts/api/packet.py
index bf90961c26..873b8f0324 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -175,7 +175,7 @@ def adjust_addresses(packets: list[Packet], expected: bool = False) -> list[Pack
Raises:
InternalError: If no tests are running.
"""
- from framework.test_suite import TestSuite
+ from api.test_suite import TestSuite
if get_ctx().local.current_test_suite is None:
raise InternalError("No current test suite, tests aren't running?")
diff --git a/dts/framework/test_suite.py b/dts/api/test_suite.py
similarity index 99%
rename from dts/framework/test_suite.py
rename to dts/api/test_suite.py
index 786cfc7bff..0822f9bfe5 100644
--- a/dts/framework/test_suite.py
+++ b/dts/api/test_suite.py
@@ -34,8 +34,7 @@
from api.testbed_model.topology import Topology
from api.utils import to_pascal_case
from framework.config.common import FrozenModel
-
-from .logger import DTSLogger, get_dts_logger
+from framework.logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
from api.context import Context
diff --git a/dts/api/testbed_model/capability.py b/dts/api/testbed_model/capability.py
index 4e4e976be5..95583261d8 100644
--- a/dts/api/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -25,7 +25,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -36,7 +36,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -71,8 +71,8 @@ def test_scatter_mbuf_2048(self):
from framework.logger import get_dts_logger
if TYPE_CHECKING:
+ from api.test_suite import TestCase
from api.testpmd import TestPmd
- from framework.test_suite import TestCase
P = ParamSpec("P")
TestPmdMethod = Callable[Concatenate["TestPmd", P], Any]
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index 566dc7c4a2..3a3580aaf7 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -43,7 +43,7 @@
# Import only if type checking or building docs, to prevent circular imports.
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig
+ from api.test_suite import BaseConfig
NodesConfig = Annotated[list[NodeConfiguration], Field(min_length=1)]
@@ -182,7 +182,7 @@ def load_config(ctx: ValidationContext) -> Configuration:
nodes = _load_and_parse_model(ctx["settings"].nodes_config_path, NodesConfig, ctx)
try:
- from framework.test_suite import BaseConfig as BaseConfig
+ from api.test_suite import BaseConfig as BaseConfig
Configuration.model_rebuild()
return Configuration.model_validate(
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 977067f42a..81e3dba79b 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -33,7 +33,7 @@
from .common import FrozenModel, load_fields_from_settings
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
+ from api.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
@unique
@@ -230,7 +230,7 @@ class TestSuiteConfig(FrozenModel):
@cached_property
def test_suite_spec(self) -> "TestSuiteSpec":
"""The specification of the requested test suite."""
- from framework.test_suite import find_by_name
+ from api.test_suite import find_by_name
test_suite_spec = find_by_name(self.test_suite_name)
assert (
@@ -280,7 +280,7 @@ def fetch_all_test_suites() -> list[TestSuiteConfig]:
This function does not include the smoke tests.
"""
- from framework.test_suite import AVAILABLE_TEST_SUITES
+ from api.test_suite import AVAILABLE_TEST_SUITES
return [
TestSuiteConfig(test_suite=test_suite.name)
@@ -506,7 +506,7 @@ def filter_tests(
self, tests_config: dict[str, "BaseConfig"]
) -> Iterable[tuple[type["TestSuite"], "BaseConfig", deque[type["TestCase"]]]]:
"""Filter test suites and cases selected for execution."""
- from framework.test_suite import TestCaseType
+ from api.test_suite import TestCaseType
test_suites = [TestSuiteConfig(test_suite="smoke_tests")]
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 790fbf997d..c133fbecb0 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -108,6 +108,7 @@
from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
+from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from api.testbed_model.capability import (
Capability,
get_supported_capabilities,
@@ -121,7 +122,6 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index 97e03b8fb7..31e69c0de9 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,9 +11,9 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.port import Port
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_checksum_offload.py b/dts/tests/TestSuite_checksum_offload.py
index 90ca798e56..a2ea13991b 100644
--- a/dts/tests/TestSuite_checksum_offload.py
+++ b/dts/tests/TestSuite_checksum_offload.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import ChecksumOffloadOptions, PacketOffloadFlag
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_IPV4_CKSUM)
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index fc4b3cb308..d2a6cbab94 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -32,8 +32,8 @@
)
from api.exception import SkippedTestException
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
-from framework.test_suite import BaseConfig, TestSuite, crypto_test
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_dual_vlan.py b/dts/tests/TestSuite_dual_vlan.py
index 1b77dd2b47..f3347a6d52 100644
--- a/dts/tests/TestSuite_dual_vlan.py
+++ b/dts/tests/TestSuite_dual_vlan.py
@@ -21,9 +21,9 @@
from api.capabilities import LinkTopology, requires_link_topology
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestDualVlan(TestSuite):
diff --git a/dts/tests/TestSuite_dynamic_config.py b/dts/tests/TestSuite_dynamic_config.py
index 7204ec4f73..b9e2c30da1 100644
--- a/dts/tests/TestSuite_dynamic_config.py
+++ b/dts/tests/TestSuite_dynamic_config.py
@@ -27,9 +27,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index b62efa2b42..24584c7d60 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -38,9 +38,9 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
def setup_and_teardown_test(
diff --git a/dts/tests/TestSuite_hello_world.py b/dts/tests/TestSuite_hello_world.py
index bf1a93c782..cd62eb8f3e 100644
--- a/dts/tests/TestSuite_hello_world.py
+++ b/dts/tests/TestSuite_hello_world.py
@@ -9,8 +9,8 @@
"""
from api.test import log
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 5650366c36..f237821a04 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -19,11 +19,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index b44822d31c..eb1413f336 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -26,8 +26,8 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mtu.py b/dts/tests/TestSuite_mtu.py
index 8355495d33..c264db299e 100644
--- a/dts/tests/TestSuite_mtu.py
+++ b/dts/tests/TestSuite_mtu.py
@@ -23,8 +23,8 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
STANDARD_FRAME = 1518 # --max-pkt-len will subtract l2 information at a minimum of 18 bytes.
JUMBO_FRAME = 9018
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 042b7019aa..fd5cef5268 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,6 +36,7 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -44,7 +45,6 @@
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
-from framework.test_suite import TestSuite, func_test
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py
index 96da67ee7d..6c7f6d79fe 100644
--- a/dts/tests/TestSuite_pmd_buffer_scatter.py
+++ b/dts/tests/TestSuite_pmd_buffer_scatter.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 4df273e3e1..162e08ccbc 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -23,6 +23,7 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import (
@@ -31,7 +32,6 @@
TestPmdVerbosePacket,
)
from api.utils import StrEnum
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_port_control.py b/dts/tests/TestSuite_port_control.py
index 6be47838d0..5b960cb3a3 100644
--- a/dts/tests/TestSuite_port_control.py
+++ b/dts/tests/TestSuite_port_control.py
@@ -18,9 +18,9 @@
)
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_port_restart_config_persistency.py b/dts/tests/TestSuite_port_restart_config_persistency.py
index 4ea22b6d70..88df35d33c 100644
--- a/dts/tests/TestSuite_port_restart_config_persistency.py
+++ b/dts/tests/TestSuite_port_restart_config_persistency.py
@@ -14,9 +14,9 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import TestPmdPortFlowCtrl
-from framework.test_suite import TestSuite, func_test
ALTERNATIVE_MTU: int = 800
STANDARD_MTU: int = 1500
diff --git a/dts/tests/TestSuite_port_stats.py b/dts/tests/TestSuite_port_stats.py
index 3dc045f847..0328c6718c 100644
--- a/dts/tests/TestSuite_port_stats.py
+++ b/dts/tests/TestSuite_port_stats.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_promisc_support.py b/dts/tests/TestSuite_promisc_support.py
index a0c65dc662..c59c8c6078 100644
--- a/dts/tests/TestSuite_promisc_support.py
+++ b/dts/tests/TestSuite_promisc_support.py
@@ -21,8 +21,8 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_qinq.py b/dts/tests/TestSuite_qinq.py
index 505d71dbc8..5dde37d4db 100644
--- a/dts/tests/TestSuite_qinq.py
+++ b/dts/tests/TestSuite_qinq.py
@@ -18,8 +18,8 @@
from api.capabilities import NicCapability, requires_nic_capability
from api.packet import send_packet_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestQinq(TestSuite):
diff --git a/dts/tests/TestSuite_queue_start_stop.py b/dts/tests/TestSuite_queue_start_stop.py
index e9048d4245..6935f395c1 100644
--- a/dts/tests/TestSuite_queue_start_stop.py
+++ b/dts/tests/TestSuite_queue_start_stop.py
@@ -24,9 +24,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.TWO_LINKS)
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 7e50a075ac..8c5c59edec 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import fail, log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import FlowRule
-from framework.test_suite import TestSuite, func_test
@dataclass
diff --git a/dts/tests/TestSuite_rx_tx_offload.py b/dts/tests/TestSuite_rx_tx_offload.py
index b0da627d3c..c8d24baaae 100644
--- a/dts/tests/TestSuite_rx_tx_offload.py
+++ b/dts/tests/TestSuite_rx_tx_offload.py
@@ -13,12 +13,12 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import (
OffloadConfiguration,
RxTxLiteralSwitch,
)
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.ONE_LINK)
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index 1e7ab7b036..f1eb435759 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -22,10 +22,10 @@
)
from api.packet import assess_performance_by_packet
from api.test import verify, write_performance_json
+from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
from framework.params.types import TestPmdParamsDict
-from framework.test_suite import BaseConfig, TestSuite, perf_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 656e2e4bb7..b3eb325fc0 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,12 +19,12 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index 91a6d4eb9f..05a6d3aa18 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,11 +18,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
from api.utils import generate_random_packets
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_uni_pkt.py b/dts/tests/TestSuite_uni_pkt.py
index 222276ce67..d83185d1b2 100644
--- a/dts/tests/TestSuite_uni_pkt.py
+++ b/dts/tests/TestSuite_uni_pkt.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
class TestUniPkt(TestSuite):
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index c649aac197..2c10478df3 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,12 +12,12 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import requires
from api.testbed_model.linux_session import LinuxSession
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestVirtioFwd(TestSuite):
diff --git a/dts/tests/TestSuite_vlan.py b/dts/tests/TestSuite_vlan.py
index 898673fc86..975e87b128 100644
--- a/dts/tests/TestSuite_vlan.py
+++ b/dts/tests/TestSuite_vlan.py
@@ -23,9 +23,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_VLAN_FILTER)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 6/7] dts: move params directory from framework to API
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (4 preceding siblings ...)
2026-04-28 18:08 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
@ 2026-04-28 18:08 ` Dean Marx
2026-04-28 18:08 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-28 18:08 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The params directory is imported in test suites such as
packet capture to use as a base class for dumpcap.
Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.params.eal.rst => api.params.eal.rst} | 2 +-
doc/api/dts/{framework.params.rst => api.params.rst} | 6 +++---
.../{framework.params.types.rst => api.params.types.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/api/cryptodev/config.py | 4 ++--
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 2 +-
dts/{framework => api}/params/types.py | 8 ++++++--
dts/api/testpmd/__init__.py | 7 ++++++-
dts/api/testpmd/config.py | 6 +++---
dts/framework/remote_session/blocking_app.py | 4 ++--
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
17 files changed, 32 insertions(+), 23 deletions(-)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (97%)
rename dts/{framework => api}/params/types.py (97%)
diff --git a/doc/api/dts/framework.params.eal.rst b/doc/api/dts/api.params.eal.rst
similarity index 79%
rename from doc/api/dts/framework.params.eal.rst
rename to doc/api/dts/api.params.eal.rst
index 6999b00233..4531cb1fe1 100644
--- a/doc/api/dts/framework.params.eal.rst
+++ b/doc/api/dts/api.params.eal.rst
@@ -3,6 +3,6 @@
eal - EAL Parameters Modelling
==============================
-.. automodule:: framework.params.eal
+.. automodule:: api.params.eal
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.params.rst b/doc/api/dts/api.params.rst
similarity index 71%
rename from doc/api/dts/framework.params.rst
rename to doc/api/dts/api.params.rst
index d8c6af9667..3ea7f9215e 100644
--- a/doc/api/dts/framework.params.rst
+++ b/doc/api/dts/api.params.rst
@@ -3,7 +3,7 @@
params - Command Line Parameters Modelling
==========================================
-.. automodule:: framework.params
+.. automodule:: api.params
:members:
:show-inheritance:
@@ -11,5 +11,5 @@ params - Command Line Parameters Modelling
:hidden:
:maxdepth: 1
- framework.params.eal
- framework.params.types
+ api.params.eal
+ api.params.types
diff --git a/doc/api/dts/framework.params.types.rst b/doc/api/dts/api.params.types.rst
similarity index 80%
rename from doc/api/dts/framework.params.types.rst
rename to doc/api/dts/api.params.types.rst
index 6d609038be..4754b3a665 100644
--- a/doc/api/dts/framework.params.types.rst
+++ b/doc/api/dts/api.params.types.rst
@@ -3,6 +3,6 @@
params.types - Parameters Modelling Types
=========================================
-.. automodule:: framework.params.types
+.. automodule:: api.params.types
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 7c282bbba1..e89e782ac0 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -18,7 +18,7 @@ Packages
api
api.testbed_model
framework.remote_session
- framework.params
+ api.params
framework.config
Modules
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 1ba8e0d977..bbfe3622c2 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -27,7 +27,7 @@
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
- from framework.params.types import CryptoPmdParamsDict
+ from api.params.types import CryptoPmdParamsDict
from pathlib import PurePath
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index a88e70d45c..3420c2fe91 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.params import Params, Switch
+from api.params.eal import EalParams
from api.utils import StrEnum
-from framework.params import Params, Switch
-from framework.params.eal import EalParams
Silent = Literal[""]
diff --git a/dts/framework/params/__init__.py b/dts/api/params/__init__.py
similarity index 100%
rename from dts/framework/params/__init__.py
rename to dts/api/params/__init__.py
diff --git a/dts/framework/params/eal.py b/dts/api/params/eal.py
similarity index 97%
rename from dts/framework/params/eal.py
rename to dts/api/params/eal.py
index 86bfd3fcc6..64fa45ae12 100644
--- a/dts/framework/params/eal.py
+++ b/dts/api/params/eal.py
@@ -6,9 +6,9 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.params import Params, Switch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.virtual_device import VirtualDevice
-from framework.params import Params, Switch
if TYPE_CHECKING:
from api.testbed_model.port import Port
diff --git a/dts/framework/params/types.py b/dts/api/params/types.py
similarity index 97%
rename from dts/framework/params/types.py
rename to dts/api/params/types.py
index f2fa69f8b8..2c215cfe54 100644
--- a/dts/framework/params/types.py
+++ b/dts/api/params/types.py
@@ -12,8 +12,10 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
params = TestPmdParams(**kwargs)
"""
+from __future__ import annotations
+
from pathlib import PurePath
-from typing import TypedDict
+from typing import TYPE_CHECKING, TypedDict
from api.cryptodev.config import (
AeadAlgName,
@@ -56,7 +58,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TXRingParams,
TxUDPPortPair,
)
-from framework.params import Switch, YesNoSwitch
+
+if TYPE_CHECKING:
+ from api.params import Switch, YesNoSwitch
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9f47a15433..85ebe2ea65 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -14,6 +14,8 @@
testpmd.close()
"""
+from __future__ import annotations
+
import functools
import re
import time
@@ -21,6 +23,7 @@
from enum import Flag
from pathlib import PurePath
from typing import (
+ TYPE_CHECKING,
Any,
Callable,
ClassVar,
@@ -56,7 +59,9 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.params.types import TestPmdParamsDict
+
+if TYPE_CHECKING:
+ from api.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
from framework.settings import SETTINGS
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index 8b688834ee..96fe5e79fb 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,8 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
-from api.utils import StrEnum
-from framework.params import (
+from api.params import (
Params,
Switch,
YesNoSwitch,
@@ -24,7 +23,8 @@
modify_str,
str_from_flag_value,
)
-from framework.params.eal import EalParams
+from api.params.eal import EalParams
+from api.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 84db3974b1..537d937eca 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,9 +31,9 @@
from typing_extensions import Self
from api.context import get_ctx
+from api.params import Params
+from api.params.eal import EalParams
from api.testbed_model.node import Node
-from framework.params import Params
-from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 713a564d25..afdf7526d9 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -15,6 +15,7 @@
from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
@@ -32,7 +33,6 @@
RemoteDPDKTreeLocation,
)
from framework.logger import DTSLogger, get_dts_logger
-from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index b807f9bdae..61cc4687f3 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,8 +11,8 @@
from pathlib import PurePath
from api.context import get_ctx
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCoreList
-from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index ec539bad95..f7f0669eea 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -35,9 +35,9 @@
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from api.params import Params
from api.testbed_model.node import Node
from framework.logger import DTSLogger, get_dts_logger
-from framework.params import Params
from framework.settings import SETTINGS
P = ParamSpec("P")
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index fd5cef5268..ba67c9e1c6 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -35,6 +35,7 @@
match_all_packets,
send_packets_and_capture,
)
+from api.params import Params
from api.test import verify
from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
@@ -42,7 +43,6 @@
PacketFilteringConfig,
)
from api.testpmd import TestPmd
-from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index f1eb435759..24f2cebf17 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -21,11 +21,11 @@
requires_link_topology,
)
from api.packet import assess_performance_by_packet
+from api.params.types import TestPmdParamsDict
from api.test import verify, write_performance_json
from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
-from framework.params.types import TestPmdParamsDict
class Config(BaseConfig):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 7/7] dts: separate Linux session into interface and logic
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (5 preceding siblings ...)
2026-04-28 18:08 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
@ 2026-04-28 18:08 ` Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-28 18:08 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Separate Linux session into an interface for the API,
and a logical module in the framework.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/framework.linux_session.rst | 8 +
doc/api/dts/index.rst | 1 +
dts/api/testbed_model/linux_session.py | 372 ++----------------------
dts/api/testbed_model/node.py | 8 +-
dts/framework/linux_session.py | 366 +++++++++++++++++++++++
5 files changed, 403 insertions(+), 352 deletions(-)
create mode 100644 doc/api/dts/framework.linux_session.rst
create mode 100644 dts/framework/linux_session.py
diff --git a/doc/api/dts/framework.linux_session.rst b/doc/api/dts/framework.linux_session.rst
new file mode 100644
index 0000000000..9de2e1484d
--- /dev/null
+++ b/doc/api/dts/framework.linux_session.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+framework.linux\_session
+========================
+
+.. automodule:: framework.linux_session
+ :members:
+ :show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index e89e782ac0..0dbc18b75c 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -37,6 +37,7 @@ Modules
framework.parser
api.utils
api.exception
+ framework.linux_session
Indices and tables
diff --git a/dts/api/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
index 7307b2abe2..5bcbf1ce97 100644
--- a/dts/api/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -1,367 +1,41 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2023 PANTHEON.tech s.r.o.
# Copyright(c) 2023 University of New Hampshire
+"""Linux OS session interface.
-"""Linux OS translator.
-
-Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
-compliant with POSIX standards, so this module only implements the parts that aren't.
-This intermediate module implements the common parts of mostly POSIX compliant distributions.
+Extends the base :class:`~.os_session.OSSession` with methods specific to Linux nodes.
+The concrete implementation containing all backend logic lives in the framework package.
"""
-import json
-import re
-from collections.abc import Iterable
-from functools import cached_property
+from abc import ABC, abstractmethod
from pathlib import PurePath
-from typing import TypedDict
-
-from typing_extensions import NotRequired
-
-from api.exception import (
- ConfigurationError,
- InternalError,
- RemoteCommandExecutionError,
-)
-from api.testbed_model.port import PortInfo
-from api.utils import expand_range
-
-from .cpu import LogicalCore
-from .port import Port
-from .posix_session import PosixSession
-
-
-class LshwConfigurationOutput(TypedDict):
- """The relevant parts of ``lshw``'s ``configuration`` section."""
-
- #:
- driver: str
- #:
- link: str
-
-
-class LshwOutput(TypedDict):
- """A model of the relevant information from ``lshw``'s json output.
-
- Example:
- ::
-
- {
- ...
- "businfo" : "pci@0000:08:00.0",
- "logicalname" : "enp8s0",
- "version" : "00",
- "serial" : "52:54:00:59:e1:ac",
- ...
- "configuration" : {
- ...
- "link" : "yes",
- ...
- },
- ...
- """
-
- #:
- businfo: str
- #:
- logicalname: NotRequired[str]
- #:
- serial: NotRequired[str]
- #:
- configuration: LshwConfigurationOutput
-
-
-class LinuxSession(PosixSession):
- """The implementation of non-Posix compliant parts of Linux."""
-
- @staticmethod
- def _get_privileged_command(command: str) -> str:
- command = command.replace(r"'", r"\'")
- return f"sudo -- sh -c '{command}'"
- def get_remote_cpus(self) -> list[LogicalCore]:
- """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
- cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
- lcores = []
- for cpu_line in cpu_info.splitlines():
- lcore, core, socket, node = map(int, cpu_line.split(","))
- lcores.append(LogicalCore(lcore, core, socket, node))
- return lcores
-
- def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
- """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
- return dpdk_prefix
-
- def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
-
- Raises:
- ConfigurationError: If the given `hugepage_size` is not supported by the OS.
- """
- self._logger.info("Getting Hugepage information.")
- if (
- f"hugepages-{hugepage_size}kB"
- not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
- ):
- raise ConfigurationError("hugepage size not supported by operating system")
- hugepages_total = self._get_hugepages_total(hugepage_size)
- self._numa_nodes = self._get_numa_nodes()
-
- if force_first_numa or hugepages_total < number_of:
- # when forcing numa, we need to clear existing hugepages regardless
- # of size, so they can be moved to the first numa node
- self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
- else:
- self._logger.info("Hugepages already configured.")
- self._mount_huge_pages()
-
- def _get_hugepages_total(self, hugepage_size: int) -> int:
- hugepages_total = self.send_command(
- f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
- ).stdout
- return int(hugepages_total)
-
- def _get_numa_nodes(self) -> list[int]:
- try:
- numa_count = self.send_command(
- "cat /sys/devices/system/node/online", verify=True
- ).stdout
- numa_range = expand_range(numa_count)
- except RemoteCommandExecutionError:
- # the file doesn't exist, meaning the node doesn't support numa
- numa_range = []
- return numa_range
-
- def _mount_huge_pages(self) -> None:
- self._logger.info("Re-mounting Hugepages.")
- hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
- self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
- result = self.send_command(hugapge_fs_cmd)
- if result.stdout == "":
- remote_mount_path = "/mnt/huge"
- self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
- self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
-
- def _supports_numa(self) -> bool:
- # the system supports numa if self._numa_nodes is non-empty and there are more
- # than one numa node (in the latter case it may actually support numa, but
- # there's no reason to do any numa specific configuration)
- return len(self._numa_nodes) > 1
-
- def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
- self._logger.info("Configuring Hugepages.")
- hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
- if force_first_numa and self._supports_numa():
- # clear non-numa hugepages
- self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
- hugepage_config_path = (
- f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
- f"/hugepages-{size}kB/nr_hugepages"
- )
-
- self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
-
- def get_port_info(self, pci_address: str) -> PortInfo:
- """Overrides :meth:`~.os_session.OSSession.get_port_info`.
-
- Raises:
- ConfigurationError: If the port could not be found.
- """
- bus_info = f"pci@{pci_address}"
- port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
- if port is None:
- raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
- logical_name = port.get("logicalname", "")
- mac_address = port.get("serial", "")
+class LinuxSession(ABC):
+ """Abstract interface for Linux-specific OS session operations."""
- configuration = port.get("configuration", {})
- driver = configuration.get("driver", "")
- is_link_up = configuration.get("link", "down") == "up"
-
- return PortInfo(mac_address, logical_name, driver, is_link_up)
-
- def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
-
- The :attr:`~.devbind_script_path` property must be setup in order to call this method.
- """
- ports_pci_addrs = " ".join(port.pci for port in ports)
-
- self.send_command(
- f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
- privileged=True,
- verify=True,
- )
-
- del self._lshw_net_info
-
- def bring_up_link(self, ports: Iterable[Port]) -> None:
- """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
- for port in ports:
- self.send_command(
- f"ip link set dev {port.logical_name} up", privileged=True, verify=True
- )
-
- del self._lshw_net_info
-
- def set_interface_link_up(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
- self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
-
- def delete_interface(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
- self.send_command(f"ip link delete {name}", privileged=True)
-
- @cached_property
+ @property
+ @abstractmethod
def devbind_script_path(self) -> PurePath:
- """The path to the dpdk-devbind.py script on the node.
-
- Needs to be manually assigned first in order to be used.
+ """The path to the devbind script."""
- Raises:
- InternalError: If accessed before environment setup.
- """
- raise InternalError("Accessed devbind script path before setup.")
-
- def load_vfio(self, pf_port: Port) -> None:
- """Overrides :meth:`~os_session.OSSession,load_vfio`."""
- cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
- device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
- if device and device.group(1) in ["37c8", "0435", "19e2"]:
- self.send_command(
- "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
- privileged=True,
- )
- self.send_command(
- "modprobe -r vfio_virqfd; modprobe -r vfio",
- privileged=True,
- )
- self.send_command(
- "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
- )
- self.send_command(
- "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
- privileged=True,
- )
- else:
- self.send_command("modprobe vfio-pci")
- self.refresh_lshw()
+ @devbind_script_path.setter
+ @abstractmethod
+ def devbind_script_path(self, value: PurePath) -> None:
+ """Set the devbind script path after environment setup."""
- def create_crypto_vfs(self, pf_port: list[Port]) -> None:
- """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+ @abstractmethod
+ def set_interface_link_up(self, name: str) -> None:
+ """Set the link status of an interface to up.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ name: The name of the interface.
"""
- for port in pf_port:
- self.delete_crypto_vfs(port)
- for port in pf_port:
- sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
- )
- self.send_command(
- f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
- )
-
- self.refresh_lshw()
- def create_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+ @abstractmethod
+ def delete_interface(self, name: str) -> None:
+ """Delete a virtual interface.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ name: The name of the interface to delete.
"""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- if curr_num_vfs == 0:
- self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
- self.refresh_lshw()
-
- def delete_crypto_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
- self.send_command(
- f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
- privileged=True,
- )
- self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
-
- def delete_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if curr_num_vfs == 0:
- self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
- else:
- self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
-
- def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- f"readlink {sys_bus_path}/virtfn*",
- privileged=True,
- )
- return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
- return []
-
- def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
- + f"{sys_bus_path}/virtfn*/uevent",
- privileged=True,
- )
- return pci_addrs.stdout.splitlines()
- else:
- return []
-
- @cached_property
- def _lshw_net_info(self) -> list[LshwOutput]:
- output = self.send_command("lshw -quiet -json -C network", verify=True)
- return json.loads(output.stdout)
-
- def refresh_lshw(self) -> None:
- """Force refresh of cached lshw network info."""
- if "_lshw_net_info" in self.__dict__:
- del self.__dict__["_lshw_net_info"]
- _ = self._lshw_net_info
-
- def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
- if attr_value:
- setattr(port, attr_name, attr_value)
- self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
- else:
- self._logger.warning(
- f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
- )
-
- def configure_port_mtu(self, mtu: int, port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
- self.send_command(
- f"ip link set dev {port.logical_name} mtu {mtu}",
- privileged=True,
- verify=True,
- )
-
- def configure_ipv4_forwarding(self, enable: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
- state = 1 if enable else 0
- self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
diff --git a/dts/api/testbed_model/node.py b/dts/api/testbed_model/node.py
index 40dd7f0666..51abbd098b 100644
--- a/dts/api/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -15,17 +15,19 @@
from functools import cached_property
from pathlib import PurePath
-from typing import Literal, TypeAlias
+from typing import TYPE_CHECKING, Literal, TypeAlias
from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
+
+if TYPE_CHECKING:
+ from framework.linux_session import LinuxSession
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
-from .linux_session import LinuxSession
from .os_session import OSSession, OSSessionInfo
from .port import Port
@@ -211,7 +213,7 @@ def create_session(node_config: NodeConfiguration, name: str, logger: DTSLogger)
"""
match node_config.os:
case OS.linux:
- return LinuxSession(node_config, name, logger)
+ return "LinuxSession"(node_config, name, logger)
case _:
raise ConfigurationError(f"Unsupported OS {node_config.os}")
diff --git a/dts/framework/linux_session.py b/dts/framework/linux_session.py
new file mode 100644
index 0000000000..e5320b7fc4
--- /dev/null
+++ b/dts/framework/linux_session.py
@@ -0,0 +1,366 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2023 University of New Hampshire
+
+"""Linux OS translator.
+
+Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
+compliant with POSIX standards, so this module only implements the parts that aren't.
+This intermediate module implements the common parts of mostly POSIX compliant distributions.
+"""
+
+import json
+import re
+from collections.abc import Iterable
+from functools import cached_property
+from pathlib import PurePath
+from typing import TypedDict
+
+from typing_extensions import NotRequired
+
+from api.exception import (
+ ConfigurationError,
+ InternalError,
+ RemoteCommandExecutionError,
+)
+from api.testbed_model.cpu import LogicalCore
+from api.testbed_model.linux_session import LinuxSession as LinuxSessionBase
+from api.testbed_model.port import Port, PortInfo
+from api.testbed_model.posix_session import PosixSession
+from api.utils import expand_range
+
+
+class LshwConfigurationOutput(TypedDict):
+ """The relevant parts of ``lshw``'s ``configuration`` section."""
+
+ #:
+ driver: str
+ #:
+ link: str
+
+
+class LshwOutput(TypedDict):
+ """A model of the relevant information from ``lshw``'s json output.
+
+ Example:
+ ::
+
+ {
+ ...
+ "businfo" : "pci@0000:08:00.0",
+ "logicalname" : "enp8s0",
+ "version" : "00",
+ "serial" : "52:54:00:59:e1:ac",
+ ...
+ "configuration" : {
+ ...
+ "link" : "yes",
+ ...
+ },
+ ...
+ """
+
+ #:
+ businfo: str
+ #:
+ logicalname: NotRequired[str]
+ #:
+ serial: NotRequired[str]
+ #:
+ configuration: LshwConfigurationOutput
+
+
+class LinuxSession(PosixSession, LinuxSessionBase):
+ """The implementation of non-Posix compliant parts of Linux."""
+
+ @staticmethod
+ def _get_privileged_command(command: str) -> str:
+ command = command.replace(r"'", r"\'")
+ return f"sudo -- sh -c '{command}'"
+
+ def get_remote_cpus(self) -> list[LogicalCore]:
+ """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
+ cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
+ lcores = []
+ for cpu_line in cpu_info.splitlines():
+ lcore, core, socket, node = map(int, cpu_line.split(","))
+ lcores.append(LogicalCore(lcore, core, socket, node))
+ return lcores
+
+ def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
+ """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
+ return dpdk_prefix
+
+ def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
+
+ Raises:
+ ConfigurationError: If the given `hugepage_size` is not supported by the OS.
+ """
+ self._logger.info("Getting Hugepage information.")
+ if (
+ f"hugepages-{hugepage_size}kB"
+ not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
+ ):
+ raise ConfigurationError("hugepage size not supported by operating system")
+ hugepages_total = self._get_hugepages_total(hugepage_size)
+ self._numa_nodes = self._get_numa_nodes()
+
+ if force_first_numa or hugepages_total < number_of:
+ # when forcing numa, we need to clear existing hugepages regardless
+ # of size, so they can be moved to the first numa node
+ self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
+ else:
+ self._logger.info("Hugepages already configured.")
+ self._mount_huge_pages()
+
+ def _get_hugepages_total(self, hugepage_size: int) -> int:
+ hugepages_total = self.send_command(
+ f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
+ ).stdout
+ return int(hugepages_total)
+
+ def _get_numa_nodes(self) -> list[int]:
+ try:
+ numa_count = self.send_command(
+ "cat /sys/devices/system/node/online", verify=True
+ ).stdout
+ numa_range = expand_range(numa_count)
+ except RemoteCommandExecutionError:
+ # the file doesn't exist, meaning the node doesn't support numa
+ numa_range = []
+ return numa_range
+
+ def _mount_huge_pages(self) -> None:
+ self._logger.info("Re-mounting Hugepages.")
+ hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
+ self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
+ result = self.send_command(hugapge_fs_cmd)
+ if result.stdout == "":
+ remote_mount_path = "/mnt/huge"
+ self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
+ self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
+
+ def _supports_numa(self) -> bool:
+ # the system supports numa if self._numa_nodes is non-empty and there are more
+ # than one numa node (in the latter case it may actually support numa, but
+ # there's no reason to do any numa specific configuration)
+ return len(self._numa_nodes) > 1
+
+ def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
+ self._logger.info("Configuring Hugepages.")
+ hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
+ if force_first_numa and self._supports_numa():
+ # clear non-numa hugepages
+ self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
+ hugepage_config_path = (
+ f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
+ f"/hugepages-{size}kB/nr_hugepages"
+ )
+
+ self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
+
+ def get_port_info(self, pci_address: str) -> PortInfo:
+ """Overrides :meth:`~.os_session.OSSession.get_port_info`.
+
+ Raises:
+ ConfigurationError: If the port could not be found.
+ """
+ bus_info = f"pci@{pci_address}"
+ port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
+ if port is None:
+ raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
+
+ logical_name = port.get("logicalname", "")
+ mac_address = port.get("serial", "")
+
+ configuration = port.get("configuration", {})
+ driver = configuration.get("driver", "")
+ is_link_up = configuration.get("link", "down") == "up"
+
+ return PortInfo(mac_address, logical_name, driver, is_link_up)
+
+ def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
+
+ The :attr:`~.devbind_script_path` property must be setup in order to call this method.
+ """
+ ports_pci_addrs = " ".join(port.pci for port in ports)
+
+ self.send_command(
+ f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
+ privileged=True,
+ verify=True,
+ )
+
+ del self._lshw_net_info
+
+ def bring_up_link(self, ports: Iterable[Port]) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
+ for port in ports:
+ self.send_command(
+ f"ip link set dev {port.logical_name} up", privileged=True, verify=True
+ )
+
+ del self._lshw_net_info
+
+ def set_interface_link_up(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
+ self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
+
+ def delete_interface(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
+ self.send_command(f"ip link delete {name}", privileged=True)
+
+ @cached_property
+ def devbind_script_path(self) -> PurePath:
+ """The path to the dpdk-devbind.py script on the node.
+
+ Needs to be manually assigned first in order to be used.
+
+ Raises:
+ InternalError: If accessed before environment setup.
+ """
+ raise InternalError("Accessed devbind script path before setup.")
+
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~os_session.OSSession,load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self.refresh_lshw()
+
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+
+ self.refresh_lshw()
+
+ def create_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ if curr_num_vfs == 0:
+ self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ self.refresh_lshw()
+
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
+ def delete_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if curr_num_vfs == 0:
+ self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
+ else:
+ self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
+ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
+ + f"{sys_bus_path}/virtfn*/uevent",
+ privileged=True,
+ )
+ return pci_addrs.stdout.splitlines()
+ else:
+ return []
+
+ @cached_property
+ def _lshw_net_info(self) -> list[LshwOutput]:
+ output = self.send_command("lshw -quiet -json -C network", verify=True)
+ return json.loads(output.stdout)
+
+ def refresh_lshw(self) -> None:
+ """Force refresh of cached lshw network info."""
+ if "_lshw_net_info" in self.__dict__:
+ del self.__dict__["_lshw_net_info"]
+ _ = self._lshw_net_info
+
+ def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
+ if attr_value:
+ setattr(port, attr_name, attr_value)
+ self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
+ else:
+ self._logger.warning(
+ f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
+ )
+
+ def configure_port_mtu(self, mtu: int, port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
+ self.send_command(
+ f"ip link set dev {port.logical_name} mtu {mtu}",
+ privileged=True,
+ verify=True,
+ )
+
+ def configure_ipv4_forwarding(self, enable: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
+ state = 1 if enable else 0
+ self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 0/7] dts: move test suite imports from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
` (7 preceding siblings ...)
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
@ 2026-04-30 21:09 ` Dean Marx
2026-04-30 21:09 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
` (6 more replies)
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
9 siblings, 7 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 21:09 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
This series moves various modules from the framework directory
to the API based on which are being imported by test suites.
These include:
- test_suite
- testbed_model
- exception
- utils
- context
- params
This eliminates all test suite framework imports except for
the remote_session imports in packet_capture, as well
as the settings/config imports in smoke_tests. I believe these
imports, and what to do with them, should be a topic of discussion
in future DTS meetings, as I don't believe they should reside in the
API, even if they are being imported in test suites.
In addition to these changes, I've split the linux_session module
into an interface in api/testbed_model/linux_session and an
implementation in framework/linux_session. This way, users
can still import linux session if necessary, without exposing
the backend implementation.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
v2:
* Integrated rst updates with corresponding commits for build testing
v3:
* Fixed commit ordering to resolve import inconsistency
* Updated Linux Session interface with test suite methods
v4:
* Added missing SPDX tag to framework.linux_session.rst
Dean Marx (7):
dts: move exception module from framework to API
dts: move utils from framework to API
dts: move context from framework to API
dts: move testbed model from framework to API
dts: move test suite module from framework to API
dts: move params directory from framework to API
dts: separate Linux session into interface and logic
...{framework.context.rst => api.context.rst} | 2 +-
...mework.exception.rst => api.exception.rst} | 2 +-
...work.params.eal.rst => api.params.eal.rst} | 2 +-
.../{framework.params.rst => api.params.rst} | 6 +--
....params.types.rst => api.params.types.rst} | 2 +-
...work.test_suite.rst => api.test_suite.rst} | 2 +-
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 ++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
.../{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/framework.linux_session.rst | 8 ++++
doc/api/dts/framework.testbed_model.rst | 28 -------------
...mework.testbed_model.traffic_generator.rst | 16 --------
doc/api/dts/index.rst | 13 +++---
dts/api/artifact.py | 6 +--
dts/api/capabilities.py | 14 +++----
dts/{framework => api}/context.py | 14 +++----
dts/api/cryptodev/__init__.py | 6 +--
dts/api/cryptodev/config.py | 6 +--
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 18 ++++----
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 8 ++--
dts/{framework => api}/params/types.py | 14 ++++---
dts/api/test.py | 4 +-
dts/{framework => api}/test_suite.py | 15 ++++---
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 18 ++++----
dts/{framework => api}/testbed_model/cpu.py | 4 +-
dts/api/testbed_model/linux_session.py | 41 +++++++++++++++++++
dts/{framework => api}/testbed_model/node.py | 12 +++---
.../testbed_model/os_session.py | 8 ++--
dts/{framework => api}/testbed_model/port.py | 2 +-
.../testbed_model/posix_session.py | 6 +--
.../testbed_model/topology.py | 20 ++++-----
.../traffic_generator/__init__.py | 4 +-
.../capturing_traffic_generator.py | 4 +-
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 14 +++----
.../traffic_generator/traffic_generator.py | 8 ++--
.../testbed_model/traffic_generator/trex.py | 16 ++++----
.../testbed_model/virtual_device.py | 0
dts/api/testpmd/__init__.py | 11 +++--
dts/api/testpmd/config.py | 6 +--
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 2 +-
dts/framework/config/__init__.py | 6 +--
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 12 +++---
.../{testbed_model => }/linux_session.py | 15 ++++---
dts/framework/parser.py | 2 +-
dts/framework/remote_session/blocking_app.py | 8 ++--
dts/framework/remote_session/dpdk.py | 16 ++++----
dts/framework/remote_session/dpdk_shell.py | 6 +--
.../interactive_remote_session.py | 2 +-
.../remote_session/interactive_shell.py | 8 ++--
.../remote_session/remote_session.py | 4 +-
dts/framework/runner.py | 4 +-
dts/framework/test_result.py | 5 +--
dts/framework/test_run.py | 24 +++++------
dts/tests/TestSuite_blocklist.py | 4 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 8 ++--
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 4 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 8 ++--
dts/tests/TestSuite_mac_filter.py | 4 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 12 +++---
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
dts/tests/TestSuite_pmd_rss.py | 6 +--
dts/tests/TestSuite_port_control.py | 2 +-
...stSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 12 +++---
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
.../TestSuite_single_core_forward_perf.py | 4 +-
dts/tests/TestSuite_smoke_tests.py | 6 +--
dts/tests/TestSuite_softnic.py | 6 +--
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 8 ++--
dts/tests/TestSuite_vlan.py | 2 +-
98 files changed, 359 insertions(+), 301 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
create mode 100644 doc/api/dts/framework.linux_session.rst
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/context.py (90%)
rename dts/{framework => api}/exception.py (100%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (89%)
rename dts/{framework => api}/params/types.py (94%)
rename dts/{framework => api}/test_suite.py (98%)
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (98%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
create mode 100644 dts/api/testbed_model/linux_session.py
rename dts/{framework => api}/testbed_model/node.py (96%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (98%)
rename dts/{framework => api}/testbed_model/posix_session.py (99%)
rename dts/{framework => api}/testbed_model/topology.py (96%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (95%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (91%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (96%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
rename dts/{framework => api}/utils.py (99%)
rename dts/framework/{testbed_model => }/linux_session.py (97%)
--
2.52.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v4 1/7] dts: move exception module from framework to API
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
@ 2026-04-30 21:09 ` Dean Marx
2026-04-30 21:09 ` [PATCH v4 2/7] dts: move utils " Dean Marx
` (5 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 21:09 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites currently import the exception module
from the framework in order to catch certain errors during
test execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.exception.rst => api.exception.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 2 +-
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/config/__init__.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/context.py | 2 +-
dts/framework/parser.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
.../remote_session/interactive_remote_session.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 4 ++--
dts/framework/remote_session/remote_session.py | 4 ++--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 3 +--
dts/framework/test_run.py | 4 ++--
dts/framework/test_suite.py | 2 +-
dts/framework/testbed_model/capability.py | 2 +-
dts/framework/testbed_model/linux_session.py | 2 +-
dts/framework/testbed_model/node.py | 2 +-
dts/framework/testbed_model/port.py | 2 +-
dts/framework/testbed_model/posix_session.py | 2 +-
dts/framework/testbed_model/topology.py | 2 +-
.../testbed_model/traffic_generator/__init__.py | 2 +-
dts/framework/testbed_model/traffic_generator/scapy.py | 2 +-
dts/framework/utils.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
| 2 +-
dts/tests/TestSuite_rte_flow.py | 10 +++++-----
34 files changed, 40 insertions(+), 41 deletions(-)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename dts/{framework => api}/exception.py (100%)
diff --git a/doc/api/dts/framework.exception.rst b/doc/api/dts/api.exception.rst
similarity index 77%
rename from doc/api/dts/framework.exception.rst
rename to doc/api/dts/api.exception.rst
index efb47dc5ae..8e6bff5ee7 100644
--- a/doc/api/dts/framework.exception.rst
+++ b/doc/api/dts/api.exception.rst
@@ -3,6 +3,6 @@
exception - Exceptions
======================
-.. automodule:: framework.exception
+.. automodule:: api.exception
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index c719297c11..01f630e7cd 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -36,7 +36,7 @@ Modules
framework.logger
framework.parser
framework.utils
- framework.exception
+ api.exception
Indices and tables
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 24a2b05063..7d04c7ab49 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -47,7 +47,7 @@
from paramiko import SFTPClient, SFTPFile
from typing_extensions import Buffer
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
from framework.testbed_model.node import Node, NodeIdentifier, get_node
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index a4fafc3713..c6a220dced 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -22,8 +22,8 @@
ThroughputResults,
VerifyResults,
)
+from api.exception import RemoteCommandExecutionError, SkippedTestException
from framework.context import get_ctx
-from framework.exception import RemoteCommandExecutionError, SkippedTestException
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/framework/exception.py b/dts/api/exception.py
similarity index 100%
rename from dts/framework/exception.py
rename to dts/api/exception.py
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 094a1b7a9d..cabb39a8dd 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,9 +27,9 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.exception import InternalError
from api.test import fail, log_debug
from framework.context import get_ctx
-from framework.exception import InternalError
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index e17babe0ca..9cad9a9495 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.context import get_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..9498d723d5 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
@@ -55,7 +56,6 @@
VLANOffloadFlag,
)
from framework.context import get_ctx
-from framework.exception import InteractiveCommandExecutionError, InternalError
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index d2f0138e4a..566dc7c4a2 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -35,7 +35,7 @@
from pydantic import Field, TypeAdapter, ValidationError, model_validator
from typing_extensions import Self
-from framework.exception import ConfigurationError
+from api.exception import ConfigurationError
from .common import FrozenModel, ValidationContext
from .node import NodeConfiguration
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 76e24d1785..62aaba033a 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -27,7 +27,7 @@
)
from typing_extensions import TYPE_CHECKING, Self
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 8f1021dc96..7e61c85998 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -8,7 +8,7 @@
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 3075c36857..ebf470ad30 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -15,7 +15,7 @@
from typing_extensions import Self
-from framework.exception import InternalError
+from api.exception import InternalError
class ParserFn(TypedDict):
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index c3575cfcaf..d803a9e4bd 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,6 +13,7 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.exception import ConfigurationError, RemoteFileNotFoundError
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -25,7 +26,6 @@
RemoteDPDKTreeLocation,
)
from framework.context import get_ctx
-from framework.exception import ConfigurationError, RemoteFileNotFoundError
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/interactive_remote_session.py b/dts/framework/remote_session/interactive_remote_session.py
index c8156b4345..04f45e0df8 100644
--- a/dts/framework/remote_session/interactive_remote_session.py
+++ b/dts/framework/remote_session/interactive_remote_session.py
@@ -15,8 +15,8 @@
SSHException,
)
+from api.exception import SSHConnectionError
from framework.config.node import NodeConfiguration
-from framework.exception import SSHConnectionError
from framework.logger import DTSLogger
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index a65cbce209..fdd074be3a 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,12 +29,12 @@
from paramiko import Channel, channel
from typing_extensions import Self
-from framework.context import get_ctx
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from framework.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py
index 158325bb7f..f49966070f 100644
--- a/dts/framework/remote_session/remote_session.py
+++ b/dts/framework/remote_session/remote_session.py
@@ -24,13 +24,13 @@
SSHException,
)
-from framework.config.node import NodeConfiguration
-from framework.exception import (
+from api.exception import (
RemoteCommandExecutionError,
SSHConnectionError,
SSHSessionDeadError,
SSHTimeoutError,
)
+from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.settings import SETTINGS
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index 6ea4749ff4..a0d8039a04 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -12,8 +12,8 @@
import sys
import textwrap
+from api.exception import ConfigurationError
from framework.config.common import ValidationContext
-from framework.exception import ConfigurationError
from framework.test_run import TestRun
from framework.testbed_model.node import Node
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 21faa55dc1..3cecb928ca 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -35,12 +35,11 @@
)
from typing_extensions import OrderedDict
+from api.exception import DTSError, ErrorSeverity, InternalError
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
from framework.testbed_model.os_session import OSSessionInfo
-from .exception import DTSError, ErrorSeverity, InternalError
-
class Result(IntEnum):
"""The possible states that a setup, a teardown or a test case may end up in."""
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..bbaf4f1fdf 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,9 +106,9 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
@@ -136,7 +136,7 @@ class TestRun:
If an error occurs, the current stage is aborted, the error is recorded, everything in
the inner stages is marked as blocked and the run continues in the next iteration
of the same stage. The return code is the highest `severity` of all
- :class:`~.framework.exception.DTSError`\s.
+ :class:`~.api.exception.DTSError`\s.
Example:
An error occurs in a test suite setup. The current test suite is aborted,
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 69ce26040a..e06fdd28b9 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -29,11 +29,11 @@
from scapy.packet import Packet
from typing_extensions import Self
+from api.exception import ConfigurationError, InternalError
from framework.config.common import FrozenModel
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
-from .exception import ConfigurationError, InternalError
from .logger import DTSLogger, get_dts_logger
from .utils import to_pascal_case
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 960370fc72..001b65994c 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -64,7 +64,7 @@ def test_scatter_mbuf_2048(self):
from typing_extensions import Self
from api.capabilities import LinkTopology, NicCapability
-from framework.exception import ConfigurationError, InternalError, SkippedTestException
+from api.exception import ConfigurationError, InternalError, SkippedTestException
from framework.logger import get_dts_logger
from framework.testbed_model.node import Node
from framework.testbed_model.port import DriverKind
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index ee943462c2..88b6da1ae6 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -18,7 +18,7 @@
from typing_extensions import NotRequired
-from framework.exception import (
+from api.exception import (
ConfigurationError,
InternalError,
RemoteCommandExecutionError,
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 67a96ef4e5..4f42bf6aeb 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -17,11 +17,11 @@
from pathlib import PurePath
from typing import Literal, TypeAlias
+from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
-from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
diff --git a/dts/framework/testbed_model/port.py b/dts/framework/testbed_model/port.py
index d81bc4cda0..aea3e59c25 100644
--- a/dts/framework/testbed_model/port.py
+++ b/dts/framework/testbed_model/port.py
@@ -12,8 +12,8 @@
from functools import cached_property
from typing import TYPE_CHECKING, Any, Final, Literal, NamedTuple
+from api.exception import InternalError
from framework.config.node import PortConfig
-from framework.exception import InternalError
if TYPE_CHECKING:
from .node import Node
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/framework/testbed_model/posix_session.py
index dec952685a..db2c3c0c40 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -16,7 +16,7 @@
from collections.abc import Iterable
from pathlib import Path, PurePath, PurePosixPath
-from framework.exception import DPDKBuildError, RemoteCommandExecutionError
+from api.exception import DPDKBuildError, RemoteCommandExecutionError
from framework.settings import SETTINGS
from framework.utils import (
MesonArgs,
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 34862c4d2e..805a762c19 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -18,7 +18,7 @@
from typing_extensions import Self
from api.capabilities import LinkTopology
-from framework.exception import ConfigurationError, InternalError
+from api.exception import ConfigurationError, InternalError
from framework.testbed_model.linux_session import LinuxSession
from framework.testbed_model.node import Node, NodeIdentifier
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/framework/testbed_model/traffic_generator/__init__.py
index fca251f534..324b5e88f3 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/framework/testbed_model/traffic_generator/__init__.py
@@ -14,12 +14,12 @@
and a capturing traffic generator is required.
"""
+from api.exception import ConfigurationError
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.exception import ConfigurationError
from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/framework/testbed_model/traffic_generator/scapy.py
index c6e9006205..e983443548 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/framework/testbed_model/traffic_generator/scapy.py
@@ -25,9 +25,9 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet
+from api.exception import InteractiveSSHSessionDeadError, InternalError
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
-from framework.exception import InteractiveSSHSessionDeadError, InternalError
from framework.remote_session.python_shell import PythonShell
from framework.testbed_model.node import Node
from framework.testbed_model.port import Port
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 9917ffbfaa..28e344871a 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -26,7 +26,7 @@
from scapy.layers.inet import IP, TCP, UDP, Ether
from scapy.packet import Packet
-from .exception import InternalError
+from api.exception import InternalError
REGEX_FOR_PCI_ADDRESS: str = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}"
_REGEX_FOR_COLON_OR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}[:-]){5}[\da-fA-F]{2}"
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index af0a5680ab..f36b48a153 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -29,9 +29,9 @@
from api.cryptodev.types import (
CryptodevResults,
)
+from api.exception import SkippedTestException
from api.test import verify
from framework.context import get_ctx
-from framework.exception import SkippedTestException
from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index 5ac85bee7d..b62efa2b42 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -35,11 +35,11 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import TestSuite, func_test
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index a7e24b37d5..b44822d31c 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -23,10 +23,10 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
from api.testpmd import TestPmd
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import TestSuite, func_test
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index f6adf262c3..1e5a6860be 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -20,6 +20,7 @@
requires_link_topology,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
from api.testpmd import TestPmd
@@ -29,7 +30,6 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import BaseConfig, TestSuite, func_test
from framework.utils import StrEnum
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 6255e4c36d..7e50a075ac 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -21,15 +21,15 @@
from scapy.packet import Packet, Raw
from api.capabilities import NicCapability, requires_nic_capability
-from api.packet import send_packet_and_capture
-from api.test import fail, log, verify
-from api.testpmd import TestPmd
-from api.testpmd.types import FlowRule
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
SkippedTestException,
TestCaseVerifyError,
)
+from api.packet import send_packet_and_capture
+from api.test import fail, log, verify
+from api.testpmd import TestPmd
+from api.testpmd.types import FlowRule
from framework.test_suite import TestSuite, func_test
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 2/7] dts: move utils from framework to API
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-30 21:09 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
@ 2026-04-30 21:09 ` Dean Marx
2026-04-30 21:09 ` [PATCH v4 3/7] dts: move context " Dean Marx
` (4 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 21:09 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The utils module is used to generate a set of random
packets in certain test suites. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/config.py | 2 +-
dts/api/packet.py | 2 +-
dts/api/testpmd/config.py | 2 +-
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 0
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/test_suite.py | 2 +-
dts/framework/testbed_model/cpu.py | 2 +-
dts/framework/testbed_model/linux_session.py | 2 +-
dts/framework/testbed_model/os_session.py | 2 +-
dts/framework/testbed_model/posix_session.py | 4 ++--
.../traffic_generator/capturing_traffic_generator.py | 2 +-
.../testbed_model/traffic_generator/traffic_generator.py | 2 +-
dts/framework/testbed_model/traffic_generator/trex.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
| 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
22 files changed, 22 insertions(+), 22 deletions(-)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
rename dts/{framework => api}/utils.py (100%)
diff --git a/doc/api/dts/framework.utils.rst b/doc/api/dts/api.utils.rst
similarity index 80%
rename from doc/api/dts/framework.utils.rst
rename to doc/api/dts/api.utils.rst
index cc06d4c3c3..0b4baff1b5 100644
--- a/doc/api/dts/framework.utils.rst
+++ b/doc/api/dts/api.utils.rst
@@ -3,6 +3,6 @@
utils - Various Utilities
=========================
-.. automodule:: framework.utils
+.. automodule:: api.utils
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 01f630e7cd..783270f6e9 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -35,7 +35,7 @@ Modules
framework.context
framework.logger
framework.parser
- framework.utils
+ api.utils
api.exception
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index 69ff7aa59a..a88e70d45c 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.utils import StrEnum
from framework.params import Params, Switch
from framework.params.eal import EalParams
-from framework.utils import StrEnum
Silent = Literal[""]
diff --git a/dts/api/packet.py b/dts/api/packet.py
index cabb39a8dd..b7a9bb28bf 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -29,6 +29,7 @@
from api.exception import InternalError
from api.test import fail, log_debug
+from api.utils import get_packet_summaries
from framework.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -36,7 +37,6 @@
from framework.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
-from framework.utils import get_packet_summaries
def send_packet_and_capture(
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index e71a3e1ef0..8b688834ee 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,6 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
+from api.utils import StrEnum
from framework.params import (
Params,
Switch,
@@ -24,7 +25,6 @@
str_from_flag_value,
)
from framework.params.eal import EalParams
-from framework.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..5c847b4bd6 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -15,8 +15,8 @@
from typing_extensions import Self
+from api.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
from framework.parser import ParserFn, TextParser
-from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
RxTxLiteralSwitch = Literal["rx", "tx"]
diff --git a/dts/framework/utils.py b/dts/api/utils.py
similarity index 100%
rename from dts/framework/utils.py
rename to dts/api/utils.py
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 792290f11f..28f23389a7 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -14,7 +14,7 @@
from pydantic import Field, model_validator
from typing_extensions import Self
-from framework.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
+from api.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
from .common import FrozenModel
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 62aaba033a..977067f42a 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -28,7 +28,7 @@
from typing_extensions import TYPE_CHECKING, Self
from api.exception import InternalError
-from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
+from api.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index d803a9e4bd..69b47b823b 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -14,6 +14,7 @@
from typing import ClassVar, Final
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -33,7 +34,6 @@
from framework.testbed_model.node import Node
from framework.testbed_model.os_session import OSSession
from framework.testbed_model.virtual_device import VirtualDevice
-from framework.utils import MesonArgs, TarCompressionFormat
@dataclass(slots=True, frozen=True)
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index e06fdd28b9..426c98fdf6 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -30,12 +30,12 @@
from typing_extensions import Self
from api.exception import ConfigurationError, InternalError
+from api.utils import to_pascal_case
from framework.config.common import FrozenModel
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
from .logger import DTSLogger, get_dts_logger
-from .utils import to_pascal_case
if TYPE_CHECKING:
from framework.context import Context
diff --git a/dts/framework/testbed_model/cpu.py b/dts/framework/testbed_model/cpu.py
index 6e2ecca080..52ef196f84 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/framework/testbed_model/cpu.py
@@ -24,7 +24,7 @@
from dataclasses import dataclass
from enum import auto, unique
-from framework.utils import StrEnum, expand_range
+from api.utils import StrEnum, expand_range
@unique
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 88b6da1ae6..69b0923744 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -23,8 +23,8 @@
InternalError,
RemoteCommandExecutionError,
)
+from api.utils import expand_range
from framework.testbed_model.port import PortInfo
-from framework.utils import expand_range
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index 2c267afed1..7bb339fab2 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -29,12 +29,12 @@
from enum import Flag, auto
from pathlib import Path, PurePath, PurePosixPath
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.remote_session.interactive_remote_session import InteractiveRemoteSession
from framework.remote_session.remote_session import CommandResult, RemoteSession
from framework.settings import SETTINGS
-from framework.utils import MesonArgs, TarCompressionFormat
from .cpu import Architecture, LogicalCore
from .port import Port, PortInfo
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/framework/testbed_model/posix_session.py
index db2c3c0c40..61c634dad1 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -17,14 +17,14 @@
from pathlib import Path, PurePath, PurePosixPath
from api.exception import DPDKBuildError, RemoteCommandExecutionError
-from framework.settings import SETTINGS
-from framework.utils import (
+from api.utils import (
MesonArgs,
TarCompressionFormat,
convert_to_list_of_string,
create_tarball,
extract_tarball,
)
+from framework.settings import SETTINGS
from .cpu import Architecture
from .os_session import FilePermissions, OSSession, OSSessionInfo
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
index 7655751d7e..2804d64990 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
+from api.utils import get_packet_summaries
from framework.testbed_model.port import Port
-from framework.utils import get_packet_summaries
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/framework/testbed_model/traffic_generator/traffic_generator.py
index cdda5a7c08..fedce77fdf 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/framework/testbed_model/traffic_generator/traffic_generator.py
@@ -25,7 +25,7 @@ class TrafficGenerator(ABC):
Exposes the common public methods of all traffic generators and defines private methods
that must implement the traffic generation logic in subclasses. This class also extends from
- :class:`framework.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
+ :class:`api.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
from multiple classes to fulfil the traffic generating functionality without breaking
single inheritance.
"""
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/framework/testbed_model/traffic_generator/trex.py
index 22cd20dea9..2064703fcc 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/framework/testbed_model/traffic_generator/trex.py
@@ -11,6 +11,7 @@
from scapy.packet import Packet
+from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
@@ -23,7 +24,6 @@
PerformanceTrafficGenerator,
PerformanceTrafficStats,
)
-from framework.utils import StrEnum
@dataclass(slots=True)
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 596b892730..1e99b82b8c 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -20,10 +20,10 @@
)
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
+from api.utils import generate_random_packets
from framework.context import filter_cores
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 1e5a6860be..4df273e3e1 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -30,8 +30,8 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
+from api.utils import StrEnum
from framework.test_suite import BaseConfig, TestSuite, func_test
-from framework.utils import StrEnum
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 271ad4301c..fce83604a6 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -20,11 +20,11 @@
)
from api.test import verify
from api.testpmd import TestPmd
+from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.linux_session import LinuxSession
-from framework.utils import REGEX_FOR_PCI_ADDRESS
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index fa91f7ee2f..c57a12c932 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -20,9 +20,9 @@
)
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
+from api.utils import generate_random_packets
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.virtual_device import VirtualDevice
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 3/7] dts: move context from framework to API
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-30 21:09 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
2026-04-30 21:09 ` [PATCH v4 2/7] dts: move utils " Dean Marx
@ 2026-04-30 21:09 ` Dean Marx
2026-04-30 21:09 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
` (3 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 21:09 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
A couple test suites import and get the run context
during execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.context.rst => api.context.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/{framework => api}/context.py | 0
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 4 ++--
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/framework/test_run.py | 2 +-
dts/framework/test_suite.py | 4 ++--
dts/framework/testbed_model/node.py | 2 +-
dts/framework/testbed_model/topology.py | 14 +++++++-------
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
18 files changed, 25 insertions(+), 25 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename dts/{framework => api}/context.py (100%)
diff --git a/doc/api/dts/framework.context.rst b/doc/api/dts/api.context.rst
similarity index 80%
rename from doc/api/dts/framework.context.rst
rename to doc/api/dts/api.context.rst
index 925c160360..65b4ab9a47 100644
--- a/doc/api/dts/framework.context.rst
+++ b/doc/api/dts/api.context.rst
@@ -3,6 +3,6 @@
context - DTS execution context
===============================
-.. automodule:: framework.context
+.. automodule:: api.context
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 783270f6e9..98269d6e80 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -32,7 +32,7 @@ Modules
framework.test_suite
framework.test_result
framework.settings
- framework.context
+ api.context
framework.logger
framework.parser
api.utils
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 7d04c7ab49..f3dd07de56 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -86,7 +86,7 @@ def make_file_path(
path /= custom_path
else:
- from framework.context import get_ctx
+ from api.context import get_ctx
try:
ctx = get_ctx()
diff --git a/dts/framework/context.py b/dts/api/context.py
similarity index 100%
rename from dts/framework/context.py
rename to dts/api/context.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index c6a220dced..67dcb02130 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -23,7 +23,7 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index b7a9bb28bf..59f26da833 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -30,7 +30,7 @@
from api.exception import InternalError
from api.test import fail, log_debug
from api.utils import get_packet_summaries
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
@@ -82,7 +82,7 @@ def send_packets_and_capture(
Returns:
A list of received packets.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index 9cad9a9495..a1f2326075 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -11,7 +11,7 @@
from api.artifact import Artifact
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9498d723d5..a528663c21 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -55,7 +55,7 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index c3b02dcc62..07db6dfeb0 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -30,7 +30,7 @@
from typing_extensions import Self
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 69b47b823b..9f7cd60dfe 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -26,7 +26,7 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 269c2cada4..a8f169787c 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -10,7 +10,7 @@
from abc import ABC, abstractmethod
from pathlib import PurePath
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index fdd074be3a..d138727c85 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -34,7 +34,7 @@
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index bbaf4f1fdf..605a916ebb 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -108,7 +108,7 @@
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.config.test_run import TestRunConfiguration
-from framework.context import Context, init_ctx
+from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 426c98fdf6..6066f7a77a 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -38,7 +38,7 @@
from .logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
- from framework.context import Context
+ from api.context import Context
class BaseConfig(FrozenModel):
@@ -92,7 +92,7 @@ def __init__(self, config: BaseConfig) -> None:
Args:
config: The test suite configuration.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self.config = config
self._ctx = get_ctx()
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 4f42bf6aeb..40dd7f0666 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -242,7 +242,7 @@ def get_node(node_identifier: NodeIdentifier) -> Node | None:
if node_identifier == "local":
return None
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
if node_identifier == "sut":
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 805a762c19..5b6ff2add5 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -96,7 +96,7 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
Raises:
InternalError: If the given `node_identifier` is invalid.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
match node_identifier:
@@ -180,7 +180,7 @@ def instantiate_crypto_ports(self) -> None:
Raises:
InternalError: If crypto virtual functions could not be created on a port.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
for port in ctx.sut_node.cryptodevs:
@@ -206,7 +206,7 @@ def instantiate_vf_ports(self) -> None:
Raises:
InternalError: If virtual function creation fails.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -235,7 +235,7 @@ def instantiate_vf_ports(self) -> None:
def delete_vf_ports(self) -> None:
"""Delete virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -246,7 +246,7 @@ def delete_vf_ports(self) -> None:
def delete_crypto_vf_ports(self) -> None:
"""Delete crypto virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -259,7 +259,7 @@ def bind_cryptodevs(self, driver: DriverKind):
Args:
driver: The driver to bind the crypto functions
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
@@ -318,7 +318,7 @@ def _prepare_devbind_script(self) -> None:
Raises:
InternalError: If dpdk-devbind.py could not be found.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
local_script_path = Path("..", "usertools", "dpdk-devbind.py").resolve()
valid_script_path = local_script_path.exists()
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index f36b48a153..39784cbcac 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -31,7 +31,7 @@
)
from api.exception import SkippedTestException
from api.test import verify
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 1e99b82b8c..c018efb1f7 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -21,7 +21,7 @@
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.context import filter_cores
+from api.context import filter_cores
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 4/7] dts: move testbed model from framework to API
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (2 preceding siblings ...)
2026-04-30 21:09 ` [PATCH v4 3/7] dts: move context " Dean Marx
@ 2026-04-30 21:09 ` Dean Marx
2026-04-30 21:09 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
` (2 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 21:09 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites import modules from testbed model
in the framework. Move this directory to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 +++++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
doc/api/dts/framework.testbed_model.rst | 28 -------------------
...mework.testbed_model.traffic_generator.rst | 16 -----------
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/capabilities.py | 8 +++---
dts/api/context.py | 10 +++----
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 12 ++++----
dts/api/test.py | 2 +-
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 10 +++----
dts/{framework => api}/testbed_model/cpu.py | 2 +-
.../testbed_model/linux_session.py | 2 +-
dts/{framework => api}/testbed_model/node.py | 0
.../testbed_model/os_session.py | 6 ++--
dts/{framework => api}/testbed_model/port.py | 0
.../testbed_model/posix_session.py | 0
.../testbed_model/topology.py | 4 +--
.../traffic_generator/__init__.py | 2 +-
.../capturing_traffic_generator.py | 2 +-
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 12 ++++----
.../traffic_generator/traffic_generator.py | 6 ++--
.../testbed_model/traffic_generator/trex.py | 14 +++++-----
.../testbed_model/virtual_device.py | 0
dts/api/testpmd/__init__.py | 2 +-
dts/framework/params/eal.py | 6 ++--
dts/framework/params/types.py | 6 ++--
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 10 +++----
dts/framework/remote_session/dpdk_shell.py | 2 +-
.../remote_session/interactive_shell.py | 4 +--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 2 +-
dts/framework/test_run.py | 18 ++++++------
dts/framework/test_suite.py | 4 +--
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 4 +--
dts/tests/TestSuite_l2fwd.py | 4 +--
dts/tests/TestSuite_packet_capture.py | 8 +++---
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 6 ++--
57 files changed, 148 insertions(+), 148 deletions(-)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (99%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
rename dts/{framework => api}/testbed_model/linux_session.py (99%)
rename dts/{framework => api}/testbed_model/node.py (100%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (100%)
rename dts/{framework => api}/testbed_model/posix_session.py (100%)
rename dts/{framework => api}/testbed_model/topology.py (99%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (97%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (99%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (94%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (97%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
diff --git a/doc/api/dts/framework.testbed_model.capability.rst b/doc/api/dts/api.testbed_model.capability.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.capability.rst
rename to doc/api/dts/api.testbed_model.capability.rst
index fab91cad83..88e396dddb 100644
--- a/doc/api/dts/framework.testbed_model.capability.rst
+++ b/doc/api/dts/api.testbed_model.capability.rst
@@ -3,6 +3,6 @@
capability - Testbed Capabilities
=================================
-.. automodule:: framework.testbed_model.capability
+.. automodule:: api.testbed_model.capability
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.cpu.rst b/doc/api/dts/api.testbed_model.cpu.rst
similarity index 78%
rename from doc/api/dts/framework.testbed_model.cpu.rst
rename to doc/api/dts/api.testbed_model.cpu.rst
index 997f2a9795..dbbb29480a 100644
--- a/doc/api/dts/framework.testbed_model.cpu.rst
+++ b/doc/api/dts/api.testbed_model.cpu.rst
@@ -3,6 +3,6 @@
cpu - CPU Representation and Utilities
======================================
-.. automodule:: framework.testbed_model.cpu
+.. automodule:: api.testbed_model.cpu
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.linux_session.rst b/doc/api/dts/api.testbed_model.linux_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.linux_session.rst
rename to doc/api/dts/api.testbed_model.linux_session.rst
index 7567816199..cfe79d8bca 100644
--- a/doc/api/dts/framework.testbed_model.linux_session.rst
+++ b/doc/api/dts/api.testbed_model.linux_session.rst
@@ -3,6 +3,6 @@
linux\_session - Linux Remote Session
=====================================
-.. automodule:: framework.testbed_model.linux_session
+.. automodule:: api.testbed_model.linux_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.node.rst b/doc/api/dts/api.testbed_model.node.rst
similarity index 71%
rename from doc/api/dts/framework.testbed_model.node.rst
rename to doc/api/dts/api.testbed_model.node.rst
index 23c6c46a00..15f522e5f7 100644
--- a/doc/api/dts/framework.testbed_model.node.rst
+++ b/doc/api/dts/api.testbed_model.node.rst
@@ -3,6 +3,6 @@
node - Base Node
================
-.. automodule:: framework.testbed_model.node
+.. automodule:: api.testbed_model.node
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.os_session.rst b/doc/api/dts/api.testbed_model.os_session.rst
similarity index 76%
rename from doc/api/dts/framework.testbed_model.os_session.rst
rename to doc/api/dts/api.testbed_model.os_session.rst
index ecfb352311..e7e3f9894f 100644
--- a/doc/api/dts/framework.testbed_model.os_session.rst
+++ b/doc/api/dts/api.testbed_model.os_session.rst
@@ -3,6 +3,6 @@
os\_session - OS-aware Remote Session ABC
=========================================
-.. automodule:: framework.testbed_model.os_session
+.. automodule:: api.testbed_model.os_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.port.rst b/doc/api/dts/api.testbed_model.port.rst
similarity index 77%
rename from doc/api/dts/framework.testbed_model.port.rst
rename to doc/api/dts/api.testbed_model.port.rst
index fdb7ca8a1d..d64501aef0 100644
--- a/doc/api/dts/framework.testbed_model.port.rst
+++ b/doc/api/dts/api.testbed_model.port.rst
@@ -3,7 +3,7 @@
port - NIC Port Representation
==============================
-.. automodule:: framework.testbed_model.port
+.. automodule:: api.testbed_model.port
:members:
:show-inheritance:
:noindex:
diff --git a/doc/api/dts/framework.testbed_model.posix_session.rst b/doc/api/dts/api.testbed_model.posix_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.posix_session.rst
rename to doc/api/dts/api.testbed_model.posix_session.rst
index e65585fd85..9f0e9ff18d 100644
--- a/doc/api/dts/framework.testbed_model.posix_session.rst
+++ b/doc/api/dts/api.testbed_model.posix_session.rst
@@ -3,6 +3,6 @@
posix\_session - Posix Remote Session
=====================================
-.. automodule:: framework.testbed_model.posix_session
+.. automodule:: api.testbed_model.posix_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.rst b/doc/api/dts/api.testbed_model.rst
new file mode 100644
index 0000000000..5e2e5189b2
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.rst
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+testbed\_model - Testbed Modelling Package
+==========================================
+
+.. automodule:: api.testbed_model
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 2
+
+ api.testbed_model.traffic_generator
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.os_session
+ api.testbed_model.linux_session
+ api.testbed_model.posix_session
+ api.testbed_model.node
+ api.testbed_model.capability
+ api.testbed_model.cpu
+ api.testbed_model.port
+ api.testbed_model.topology
+ api.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.topology.rst b/doc/api/dts/api.testbed_model.topology.rst
similarity index 73%
rename from doc/api/dts/framework.testbed_model.topology.rst
rename to doc/api/dts/api.testbed_model.topology.rst
index 496f2a895f..bb63fe38dc 100644
--- a/doc/api/dts/framework.testbed_model.topology.rst
+++ b/doc/api/dts/api.testbed_model.topology.rst
@@ -3,6 +3,6 @@
topology - Testbed Topology
===========================
-.. automodule:: framework.testbed_model.topology
+.. automodule:: api.testbed_model.topology
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
similarity index 68%
rename from doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
index 29fa834042..cfe03201a7 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
@@ -3,6 +3,6 @@
capturing\_traffic\_generator - Base Capturing TG ABC
=====================================================
-.. automodule:: framework.testbed_model.traffic_generator.capturing_traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.capturing_traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.rst
new file mode 100644
index 0000000000..311bdcf6b9
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.traffic_generator.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+traffic\_generator Subpackage
+=============================
+
+.. automodule:: api.testbed_model.traffic_generator
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.traffic_generator.traffic_generator
+ api.testbed_model.traffic_generator.capturing_traffic_generator
+ api.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
similarity index 70%
rename from doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
index df78ac9514..949bb66632 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
@@ -3,6 +3,6 @@
scapy - Capturing Traffic Generator
===================================
-.. automodule:: framework.testbed_model.traffic_generator.scapy
+.. automodule:: api.testbed_model.traffic_generator.scapy
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
similarity index 65%
rename from doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
index bfec728dee..1045e534b5 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
@@ -3,6 +3,6 @@
traffic\_generator - Base TG ABC
================================
-.. automodule:: framework.testbed_model.traffic_generator.traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.virtual_device.rst b/doc/api/dts/api.testbed_model.virtual_device.rst
similarity index 72%
rename from doc/api/dts/framework.testbed_model.virtual_device.rst
rename to doc/api/dts/api.testbed_model.virtual_device.rst
index a6b0420e75..97adc895f6 100644
--- a/doc/api/dts/framework.testbed_model.virtual_device.rst
+++ b/doc/api/dts/api.testbed_model.virtual_device.rst
@@ -3,6 +3,6 @@
virtual\_device - Virtual Devices
=================================
-.. automodule:: framework.testbed_model.virtual_device
+.. automodule:: api.testbed_model.virtual_device
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.rst b/doc/api/dts/framework.testbed_model.rst
deleted file mode 100644
index f283178f6a..0000000000
--- a/doc/api/dts/framework.testbed_model.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-testbed\_model - Testbed Modelling Package
-==========================================
-
-.. automodule:: framework.testbed_model
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 2
-
- framework.testbed_model.traffic_generator
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.os_session
- framework.testbed_model.linux_session
- framework.testbed_model.posix_session
- framework.testbed_model.node
- framework.testbed_model.capability
- framework.testbed_model.cpu
- framework.testbed_model.port
- framework.testbed_model.topology
- framework.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.rst b/doc/api/dts/framework.testbed_model.traffic_generator.rst
deleted file mode 100644
index 24c250ee3a..0000000000
--- a/doc/api/dts/framework.testbed_model.traffic_generator.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-traffic\_generator Subpackage
-=============================
-
-.. automodule:: framework.testbed_model.traffic_generator
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.traffic_generator.traffic_generator
- framework.testbed_model.traffic_generator.capturing_traffic_generator
- framework.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 98269d6e80..f47e4af3f2 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -16,7 +16,7 @@ Packages
tests
api
- framework.testbed_model
+ api.testbed_model
framework.remote_session
framework.params
framework.config
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index f3dd07de56..74a8ac667f 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -48,9 +48,9 @@
from typing_extensions import Buffer
from api.exception import InternalError
+from api.testbed_model.node import Node, NodeIdentifier, get_node
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node, NodeIdentifier, get_node
TextMode: TypeAlias = (
Literal["r", "r+", "w", "w+", "a", "a+", "x", "x+"]
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 09bc538523..04fc20738b 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -23,7 +23,7 @@
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires_link_topology
+ from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -34,7 +34,7 @@ def hello_world_single_core(self):
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires_nic_capability
+ from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -235,7 +235,7 @@ def requires_link_topology(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import TopologyCapability
+ from api.testbed_model.capability import TopologyCapability
def add_required_topology(
test_case_or_suite: type["TestProtocol"],
@@ -258,7 +258,7 @@ def requires_nic_capability(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import DecoratedNicCapability
+ from api.testbed_model.capability import DecoratedNicCapability
def add_required_capability(
test_case_or_suite: type["TestProtocol"],
diff --git a/dts/api/context.py b/dts/api/context.py
index 7e61c85998..13a2ad6c39 100644
--- a/dts/api/context.py
+++ b/dts/api/context.py
@@ -9,17 +9,17 @@
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
from api.exception import InternalError
+from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
+from api.testbed_model.node import Node
+from api.testbed_model.topology import Topology
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
-from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
+ from api.testbed_model.capability import TestProtocol
+ from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.test_suite import TestCase, TestSuite
- from framework.testbed_model.capability import TestProtocol
- from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
P = ParamSpec("P")
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 67dcb02130..1ba8e0d977 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -14,6 +14,7 @@
from typing_extensions import Unpack
+from api.context import get_ctx
from api.cryptodev.config import CryptoPmdParams, TestType
from api.cryptodev.types import (
CryptodevResults,
@@ -23,7 +24,6 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 59f26da833..bf90961c26 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,16 +27,16 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.context import get_ctx
from api.exception import InternalError
from api.test import fail, log_debug
-from api.utils import get_packet_summaries
-from api.context import get_ctx
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
+from api.utils import get_packet_summaries
def send_packet_and_capture(
@@ -83,7 +83,7 @@ def send_packets_and_capture(
A list of received packets.
"""
from api.context import get_ctx
- from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+ from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
@@ -340,7 +340,7 @@ def assess_performance_by_packet(
Returns:
Performance statistics of the generated test.
"""
- from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+ from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficGenerator,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index a1f2326075..03846639ad 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
-from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.context import get_ctx
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/framework/testbed_model/__init__.py b/dts/api/testbed_model/__init__.py
similarity index 100%
rename from dts/framework/testbed_model/__init__.py
rename to dts/api/testbed_model/__init__.py
diff --git a/dts/framework/testbed_model/capability.py b/dts/api/testbed_model/capability.py
similarity index 99%
rename from dts/framework/testbed_model/capability.py
rename to dts/api/testbed_model/capability.py
index 001b65994c..4e4e976be5 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -26,7 +26,7 @@
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires
+ from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -37,7 +37,7 @@ def hello_world_single_core(self):
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires
+ from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -65,10 +65,10 @@ def test_scatter_mbuf_2048(self):
from api.capabilities import LinkTopology, NicCapability
from api.exception import ConfigurationError, InternalError, SkippedTestException
+from api.testbed_model.node import Node
+from api.testbed_model.port import DriverKind
+from api.testbed_model.topology import Topology
from framework.logger import get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import DriverKind
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
from api.testpmd import TestPmd
diff --git a/dts/framework/testbed_model/cpu.py b/dts/api/testbed_model/cpu.py
similarity index 99%
rename from dts/framework/testbed_model/cpu.py
rename to dts/api/testbed_model/cpu.py
index 52ef196f84..ee754f5844 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/api/testbed_model/cpu.py
@@ -29,7 +29,7 @@
@unique
class Architecture(StrEnum):
- r"""The supported architectures of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported architectures of :class:`~api.testbed_model.node.Node`\s."""
#:
i686 = auto()
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
similarity index 99%
rename from dts/framework/testbed_model/linux_session.py
rename to dts/api/testbed_model/linux_session.py
index 69b0923744..7307b2abe2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -23,8 +23,8 @@
InternalError,
RemoteCommandExecutionError,
)
+from api.testbed_model.port import PortInfo
from api.utils import expand_range
-from framework.testbed_model.port import PortInfo
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/node.py b/dts/api/testbed_model/node.py
similarity index 100%
rename from dts/framework/testbed_model/node.py
rename to dts/api/testbed_model/node.py
diff --git a/dts/framework/testbed_model/os_session.py b/dts/api/testbed_model/os_session.py
similarity index 99%
rename from dts/framework/testbed_model/os_session.py
rename to dts/api/testbed_model/os_session.py
index 7bb339fab2..b1e0538ac9 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/api/testbed_model/os_session.py
@@ -73,11 +73,11 @@ class OSSessionInfo:
Attributes:
os_name: The name of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
os_version: The version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
kernel_version: The kernel version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
"""
os_name: str
diff --git a/dts/framework/testbed_model/port.py b/dts/api/testbed_model/port.py
similarity index 100%
rename from dts/framework/testbed_model/port.py
rename to dts/api/testbed_model/port.py
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/api/testbed_model/posix_session.py
similarity index 100%
rename from dts/framework/testbed_model/posix_session.py
rename to dts/api/testbed_model/posix_session.py
diff --git a/dts/framework/testbed_model/topology.py b/dts/api/testbed_model/topology.py
similarity index 99%
rename from dts/framework/testbed_model/topology.py
rename to dts/api/testbed_model/topology.py
index 5b6ff2add5..11593d64d5 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/api/testbed_model/topology.py
@@ -19,8 +19,8 @@
from api.capabilities import LinkTopology
from api.exception import ConfigurationError, InternalError
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.node import Node, NodeIdentifier
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.node import Node, NodeIdentifier
from .port import DriverKind, Port, PortConfig
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/api/testbed_model/traffic_generator/__init__.py
similarity index 97%
rename from dts/framework/testbed_model/traffic_generator/__init__.py
rename to dts/api/testbed_model/traffic_generator/__init__.py
index 324b5e88f3..11fa25448a 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/api/testbed_model/traffic_generator/__init__.py
@@ -15,12 +15,12 @@
"""
from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
similarity index 99%
rename from dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
index 2804d64990..db274e5e82 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
+from api.testbed_model.port import Port
from api.utils import get_packet_summaries
-from framework.testbed_model.port import Port
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py b/dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
similarity index 100%
rename from dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/api/testbed_model/traffic_generator/scapy.py
similarity index 98%
rename from dts/framework/testbed_model/traffic_generator/scapy.py
rename to dts/api/testbed_model/traffic_generator/scapy.py
index e983443548..215c57f93d 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/api/testbed_model/traffic_generator/scapy.py
@@ -26,15 +26,15 @@
from scapy.packet import Packet
from api.exception import InteractiveSSHSessionDeadError, InternalError
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
from .capturing_traffic_generator import CapturingTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/api/testbed_model/traffic_generator/traffic_generator.py
similarity index 94%
rename from dts/framework/testbed_model/traffic_generator/traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/traffic_generator.py
index fedce77fdf..5fd68e5144 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/traffic_generator.py
@@ -13,11 +13,11 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
from framework.config.test_run import TrafficGeneratorConfig
from framework.logger import DTSLogger, get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
class TrafficGenerator(ABC):
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/api/testbed_model/traffic_generator/trex.py
similarity index 97%
rename from dts/framework/testbed_model/traffic_generator/trex.py
rename to dts/api/testbed_model/traffic_generator/trex.py
index 2064703fcc..d97ed934c9 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/api/testbed_model/traffic_generator/trex.py
@@ -11,19 +11,19 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node, create_session
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
+ PerformanceTrafficGenerator,
+ PerformanceTrafficStats,
+)
from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node, create_session
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
- PerformanceTrafficGenerator,
- PerformanceTrafficStats,
-)
@dataclass(slots=True)
diff --git a/dts/framework/testbed_model/virtual_device.py b/dts/api/testbed_model/virtual_device.py
similarity index 100%
rename from dts/framework/testbed_model/virtual_device.py
rename to dts/api/testbed_model/virtual_device.py
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index a528663c21..9f47a15433 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.context import get_ctx
from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
@@ -55,7 +56,6 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/params/eal.py b/dts/framework/params/eal.py
index e84a20f02f..86bfd3fcc6 100644
--- a/dts/framework/params/eal.py
+++ b/dts/framework/params/eal.py
@@ -6,12 +6,12 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.virtual_device import VirtualDevice
from framework.params import Params, Switch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.virtual_device import VirtualDevice
if TYPE_CHECKING:
- from framework.testbed_model.port import Port
+ from api.testbed_model.port import Port
def _port_to_pci(port: "Port") -> str:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 3c7650474c..f2fa69f8b8 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -32,6 +32,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.port import Port
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -54,9 +57,6 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TxUDPPortPair,
)
from framework.params import Switch, YesNoSwitch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.port import Port
-from framework.testbed_model.virtual_device import VirtualDevice
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 07db6dfeb0..84db3974b1 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,11 +31,11 @@
from typing_extensions import Self
from api.context import get_ctx
+from api.testbed_model.node import Node
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
-from framework.testbed_model.node import Node
P = TypeVar("P", bound=Params)
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 9f7cd60dfe..713a564d25 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,7 +13,12 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
+from api.testbed_model.node import Node
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
@@ -26,14 +31,9 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
-from framework.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
-from framework.testbed_model.node import Node
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.virtual_device import VirtualDevice
@dataclass(slots=True, frozen=True)
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index a8f169787c..b807f9bdae 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,12 +11,12 @@
from pathlib import PurePath
from api.context import get_ctx
+from api.testbed_model.cpu import LogicalCoreList
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
)
-from framework.testbed_model.cpu import LogicalCoreList
def compute_eal_params(
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index d138727c85..ec539bad95 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,16 +29,16 @@
from paramiko import Channel, channel
from typing_extensions import Self
+from api.context import get_ctx
from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
-from api.context import get_ctx
+from api.testbed_model.node import Node
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node
P = ParamSpec("P")
T = TypeVar("T", bound="InteractiveShell")
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index a0d8039a04..29be7b80fe 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -13,9 +13,9 @@
import textwrap
from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.common import ValidationContext
from framework.test_run import TestRun
-from framework.testbed_model.node import Node
from .config import Configuration, load_config
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 3cecb928ca..5f945163ce 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -36,9 +36,9 @@
from typing_extensions import OrderedDict
from api.exception import DTSError, ErrorSeverity, InternalError
+from api.testbed_model.os_session import OSSessionInfo
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
-from framework.testbed_model.os_session import OSSessionInfo
class Result(IntEnum):
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 605a916ebb..790fbf997d 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,22 +106,22 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
+from api.testbed_model.capability import (
+ Capability,
+ get_supported_capabilities,
+ test_if_supported,
+)
+from api.testbed_model.node import Node
+from api.testbed_model.topology import PortLink, Topology
+from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
-from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
-from framework.testbed_model.capability import (
- Capability,
- get_supported_capabilities,
- test_if_supported,
-)
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import PortLink, Topology
-from framework.testbed_model.traffic_generator import create_traffic_generator
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 6066f7a77a..786cfc7bff 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -30,10 +30,10 @@
from typing_extensions import Self
from api.exception import ConfigurationError, InternalError
+from api.testbed_model.capability import TestProtocol
+from api.testbed_model.topology import Topology
from api.utils import to_pascal_case
from framework.config.common import FrozenModel
-from framework.testbed_model.capability import TestProtocol
-from framework.testbed_model.topology import Topology
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index c57231de22..97e03b8fb7 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,9 +11,9 @@
requires_link_topology,
)
from api.test import verify
+from api.testbed_model.port import Port
from api.testpmd import TestPmd
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.port import Port
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 39784cbcac..fc4b3cb308 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -13,6 +13,7 @@
LinkTopology,
requires_link_topology,
)
+from api.context import get_ctx
from api.cryptodev import Cryptodev
from api.cryptodev.config import (
AeadAlgName,
@@ -31,9 +32,8 @@
)
from api.exception import SkippedTestException
from api.test import verify
-from api.context import get_ctx
+from api.testbed_model.virtual_device import VirtualDevice
from framework.test_suite import BaseConfig, TestSuite, crypto_test
-from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index c018efb1f7..5650366c36 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -13,17 +13,17 @@
requires_link_topology,
requires_nic_capability,
)
+from api.context import filter_cores
from api.packet import (
get_expected_packets,
match_all_packets,
send_packets_and_capture,
)
+from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from api.context import filter_cores
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.cpu import LogicalCoreCount
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 4bd15e2401..042b7019aa 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,15 +36,15 @@
send_packets_and_capture,
)
from api.test import verify
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from api.testpmd import TestPmd
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index fce83604a6..656e2e4bb7 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,12 +19,12 @@
requires_link_topology,
)
from api.test import verify
+from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.linux_session import LinuxSession
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index c57a12c932..91a6d4eb9f 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,11 +18,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
from api.utils import generate_random_packets
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.virtual_device import VirtualDevice
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index bdecdb76fd..c649aac197 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,12 +12,12 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.testbed_model.capability import requires
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.capability import requires
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.virtual_device import VirtualDevice
class TestVirtioFwd(TestSuite):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 5/7] dts: move test suite module from framework to API
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (3 preceding siblings ...)
2026-04-30 21:09 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
@ 2026-04-30 21:09 ` Dean Marx
2026-04-30 21:09 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
2026-04-30 21:09 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 21:09 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Currently, each test suite imports the TestSuite class
from the DTS framework to use as a base class.
However, the goal for 26.07 is to move all test suite
imports to the API module. Moves and updates the test_suite
file to the API directory, and updates all files that import
test_suite to reflect this change.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.test_suite.rst => api.test_suite.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/capabilities.py | 6 +++---
dts/api/context.py | 2 +-
dts/api/packet.py | 2 +-
dts/{framework => api}/test_suite.py | 3 +--
dts/api/testbed_model/capability.py | 6 +++---
dts/framework/config/__init__.py | 4 ++--
dts/framework/config/test_run.py | 8 ++++----
dts/framework/test_run.py | 2 +-
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
| 2 +-
dts/tests/TestSuite_port_control.py | 2 +-
dts/tests/TestSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 2 +-
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 2 +-
dts/tests/TestSuite_vlan.py | 2 +-
37 files changed, 45 insertions(+), 46 deletions(-)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename dts/{framework => api}/test_suite.py (99%)
diff --git a/doc/api/dts/framework.test_suite.rst b/doc/api/dts/api.test_suite.rst
similarity index 81%
rename from doc/api/dts/framework.test_suite.rst
rename to doc/api/dts/api.test_suite.rst
index 9517f51a4a..4acb7b103a 100644
--- a/doc/api/dts/framework.test_suite.rst
+++ b/doc/api/dts/api.test_suite.rst
@@ -3,6 +3,6 @@
test\_suite - Common Test Suite Features
========================================
-.. automodule:: framework.test_suite
+.. automodule:: api.test_suite
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index f47e4af3f2..7c282bbba1 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -29,7 +29,7 @@ Modules
framework.runner
framework.test_run
- framework.test_suite
+ api.test_suite
framework.test_result
framework.settings
api.context
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 04fc20738b..a4d6b2b424 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -22,7 +22,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -33,7 +33,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -47,7 +47,7 @@ def test_scatter_mbuf_2048(self):
from typing import TYPE_CHECKING, Callable
if TYPE_CHECKING:
- from framework.test_suite import TestProtocol
+ from api.test_suite import TestProtocol
class LinkTopology(IntEnum):
diff --git a/dts/api/context.py b/dts/api/context.py
index 13a2ad6c39..7ed4cc5665 100644
--- a/dts/api/context.py
+++ b/dts/api/context.py
@@ -16,10 +16,10 @@
from framework.settings import SETTINGS
if TYPE_CHECKING:
+ from api.test_suite import TestCase, TestSuite
from api.testbed_model.capability import TestProtocol
from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
- from framework.test_suite import TestCase, TestSuite
P = ParamSpec("P")
diff --git a/dts/api/packet.py b/dts/api/packet.py
index bf90961c26..873b8f0324 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -175,7 +175,7 @@ def adjust_addresses(packets: list[Packet], expected: bool = False) -> list[Pack
Raises:
InternalError: If no tests are running.
"""
- from framework.test_suite import TestSuite
+ from api.test_suite import TestSuite
if get_ctx().local.current_test_suite is None:
raise InternalError("No current test suite, tests aren't running?")
diff --git a/dts/framework/test_suite.py b/dts/api/test_suite.py
similarity index 99%
rename from dts/framework/test_suite.py
rename to dts/api/test_suite.py
index 786cfc7bff..0822f9bfe5 100644
--- a/dts/framework/test_suite.py
+++ b/dts/api/test_suite.py
@@ -34,8 +34,7 @@
from api.testbed_model.topology import Topology
from api.utils import to_pascal_case
from framework.config.common import FrozenModel
-
-from .logger import DTSLogger, get_dts_logger
+from framework.logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
from api.context import Context
diff --git a/dts/api/testbed_model/capability.py b/dts/api/testbed_model/capability.py
index 4e4e976be5..95583261d8 100644
--- a/dts/api/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -25,7 +25,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -36,7 +36,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -71,8 +71,8 @@ def test_scatter_mbuf_2048(self):
from framework.logger import get_dts_logger
if TYPE_CHECKING:
+ from api.test_suite import TestCase
from api.testpmd import TestPmd
- from framework.test_suite import TestCase
P = ParamSpec("P")
TestPmdMethod = Callable[Concatenate["TestPmd", P], Any]
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index 566dc7c4a2..3a3580aaf7 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -43,7 +43,7 @@
# Import only if type checking or building docs, to prevent circular imports.
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig
+ from api.test_suite import BaseConfig
NodesConfig = Annotated[list[NodeConfiguration], Field(min_length=1)]
@@ -182,7 +182,7 @@ def load_config(ctx: ValidationContext) -> Configuration:
nodes = _load_and_parse_model(ctx["settings"].nodes_config_path, NodesConfig, ctx)
try:
- from framework.test_suite import BaseConfig as BaseConfig
+ from api.test_suite import BaseConfig as BaseConfig
Configuration.model_rebuild()
return Configuration.model_validate(
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 977067f42a..81e3dba79b 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -33,7 +33,7 @@
from .common import FrozenModel, load_fields_from_settings
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
+ from api.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
@unique
@@ -230,7 +230,7 @@ class TestSuiteConfig(FrozenModel):
@cached_property
def test_suite_spec(self) -> "TestSuiteSpec":
"""The specification of the requested test suite."""
- from framework.test_suite import find_by_name
+ from api.test_suite import find_by_name
test_suite_spec = find_by_name(self.test_suite_name)
assert (
@@ -280,7 +280,7 @@ def fetch_all_test_suites() -> list[TestSuiteConfig]:
This function does not include the smoke tests.
"""
- from framework.test_suite import AVAILABLE_TEST_SUITES
+ from api.test_suite import AVAILABLE_TEST_SUITES
return [
TestSuiteConfig(test_suite=test_suite.name)
@@ -506,7 +506,7 @@ def filter_tests(
self, tests_config: dict[str, "BaseConfig"]
) -> Iterable[tuple[type["TestSuite"], "BaseConfig", deque[type["TestCase"]]]]:
"""Filter test suites and cases selected for execution."""
- from framework.test_suite import TestCaseType
+ from api.test_suite import TestCaseType
test_suites = [TestSuiteConfig(test_suite="smoke_tests")]
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 790fbf997d..c133fbecb0 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -108,6 +108,7 @@
from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
+from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from api.testbed_model.capability import (
Capability,
get_supported_capabilities,
@@ -121,7 +122,6 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index 97e03b8fb7..31e69c0de9 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,9 +11,9 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.port import Port
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_checksum_offload.py b/dts/tests/TestSuite_checksum_offload.py
index 90ca798e56..a2ea13991b 100644
--- a/dts/tests/TestSuite_checksum_offload.py
+++ b/dts/tests/TestSuite_checksum_offload.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import ChecksumOffloadOptions, PacketOffloadFlag
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_IPV4_CKSUM)
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index fc4b3cb308..d2a6cbab94 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -32,8 +32,8 @@
)
from api.exception import SkippedTestException
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
-from framework.test_suite import BaseConfig, TestSuite, crypto_test
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_dual_vlan.py b/dts/tests/TestSuite_dual_vlan.py
index 1b77dd2b47..f3347a6d52 100644
--- a/dts/tests/TestSuite_dual_vlan.py
+++ b/dts/tests/TestSuite_dual_vlan.py
@@ -21,9 +21,9 @@
from api.capabilities import LinkTopology, requires_link_topology
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestDualVlan(TestSuite):
diff --git a/dts/tests/TestSuite_dynamic_config.py b/dts/tests/TestSuite_dynamic_config.py
index 7204ec4f73..b9e2c30da1 100644
--- a/dts/tests/TestSuite_dynamic_config.py
+++ b/dts/tests/TestSuite_dynamic_config.py
@@ -27,9 +27,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index b62efa2b42..24584c7d60 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -38,9 +38,9 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
def setup_and_teardown_test(
diff --git a/dts/tests/TestSuite_hello_world.py b/dts/tests/TestSuite_hello_world.py
index bf1a93c782..cd62eb8f3e 100644
--- a/dts/tests/TestSuite_hello_world.py
+++ b/dts/tests/TestSuite_hello_world.py
@@ -9,8 +9,8 @@
"""
from api.test import log
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 5650366c36..f237821a04 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -19,11 +19,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index b44822d31c..eb1413f336 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -26,8 +26,8 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mtu.py b/dts/tests/TestSuite_mtu.py
index 8355495d33..c264db299e 100644
--- a/dts/tests/TestSuite_mtu.py
+++ b/dts/tests/TestSuite_mtu.py
@@ -23,8 +23,8 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
STANDARD_FRAME = 1518 # --max-pkt-len will subtract l2 information at a minimum of 18 bytes.
JUMBO_FRAME = 9018
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 042b7019aa..fd5cef5268 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,6 +36,7 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -44,7 +45,6 @@
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
-from framework.test_suite import TestSuite, func_test
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py
index 96da67ee7d..6c7f6d79fe 100644
--- a/dts/tests/TestSuite_pmd_buffer_scatter.py
+++ b/dts/tests/TestSuite_pmd_buffer_scatter.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 4df273e3e1..162e08ccbc 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -23,6 +23,7 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import (
@@ -31,7 +32,6 @@
TestPmdVerbosePacket,
)
from api.utils import StrEnum
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_port_control.py b/dts/tests/TestSuite_port_control.py
index 6be47838d0..5b960cb3a3 100644
--- a/dts/tests/TestSuite_port_control.py
+++ b/dts/tests/TestSuite_port_control.py
@@ -18,9 +18,9 @@
)
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_port_restart_config_persistency.py b/dts/tests/TestSuite_port_restart_config_persistency.py
index 4ea22b6d70..88df35d33c 100644
--- a/dts/tests/TestSuite_port_restart_config_persistency.py
+++ b/dts/tests/TestSuite_port_restart_config_persistency.py
@@ -14,9 +14,9 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import TestPmdPortFlowCtrl
-from framework.test_suite import TestSuite, func_test
ALTERNATIVE_MTU: int = 800
STANDARD_MTU: int = 1500
diff --git a/dts/tests/TestSuite_port_stats.py b/dts/tests/TestSuite_port_stats.py
index 3dc045f847..0328c6718c 100644
--- a/dts/tests/TestSuite_port_stats.py
+++ b/dts/tests/TestSuite_port_stats.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_promisc_support.py b/dts/tests/TestSuite_promisc_support.py
index a0c65dc662..c59c8c6078 100644
--- a/dts/tests/TestSuite_promisc_support.py
+++ b/dts/tests/TestSuite_promisc_support.py
@@ -21,8 +21,8 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_qinq.py b/dts/tests/TestSuite_qinq.py
index 505d71dbc8..5dde37d4db 100644
--- a/dts/tests/TestSuite_qinq.py
+++ b/dts/tests/TestSuite_qinq.py
@@ -18,8 +18,8 @@
from api.capabilities import NicCapability, requires_nic_capability
from api.packet import send_packet_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestQinq(TestSuite):
diff --git a/dts/tests/TestSuite_queue_start_stop.py b/dts/tests/TestSuite_queue_start_stop.py
index e9048d4245..6935f395c1 100644
--- a/dts/tests/TestSuite_queue_start_stop.py
+++ b/dts/tests/TestSuite_queue_start_stop.py
@@ -24,9 +24,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.TWO_LINKS)
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 7e50a075ac..8c5c59edec 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import fail, log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import FlowRule
-from framework.test_suite import TestSuite, func_test
@dataclass
diff --git a/dts/tests/TestSuite_rx_tx_offload.py b/dts/tests/TestSuite_rx_tx_offload.py
index b0da627d3c..c8d24baaae 100644
--- a/dts/tests/TestSuite_rx_tx_offload.py
+++ b/dts/tests/TestSuite_rx_tx_offload.py
@@ -13,12 +13,12 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import (
OffloadConfiguration,
RxTxLiteralSwitch,
)
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.ONE_LINK)
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index 1e7ab7b036..f1eb435759 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -22,10 +22,10 @@
)
from api.packet import assess_performance_by_packet
from api.test import verify, write_performance_json
+from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
from framework.params.types import TestPmdParamsDict
-from framework.test_suite import BaseConfig, TestSuite, perf_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 656e2e4bb7..b3eb325fc0 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,12 +19,12 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index 91a6d4eb9f..05a6d3aa18 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,11 +18,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
from api.utils import generate_random_packets
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_uni_pkt.py b/dts/tests/TestSuite_uni_pkt.py
index 222276ce67..d83185d1b2 100644
--- a/dts/tests/TestSuite_uni_pkt.py
+++ b/dts/tests/TestSuite_uni_pkt.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
class TestUniPkt(TestSuite):
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index c649aac197..2c10478df3 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,12 +12,12 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import requires
from api.testbed_model.linux_session import LinuxSession
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestVirtioFwd(TestSuite):
diff --git a/dts/tests/TestSuite_vlan.py b/dts/tests/TestSuite_vlan.py
index 898673fc86..975e87b128 100644
--- a/dts/tests/TestSuite_vlan.py
+++ b/dts/tests/TestSuite_vlan.py
@@ -23,9 +23,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_VLAN_FILTER)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 6/7] dts: move params directory from framework to API
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (4 preceding siblings ...)
2026-04-30 21:09 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
@ 2026-04-30 21:09 ` Dean Marx
2026-04-30 21:09 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 21:09 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The params directory is imported in test suites such as
packet capture to use as a base class for dumpcap.
Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.params.eal.rst => api.params.eal.rst} | 2 +-
doc/api/dts/{framework.params.rst => api.params.rst} | 6 +++---
.../{framework.params.types.rst => api.params.types.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/api/cryptodev/config.py | 4 ++--
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 2 +-
dts/{framework => api}/params/types.py | 8 ++++++--
dts/api/testpmd/__init__.py | 7 ++++++-
dts/api/testpmd/config.py | 6 +++---
dts/framework/remote_session/blocking_app.py | 4 ++--
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
17 files changed, 32 insertions(+), 23 deletions(-)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (97%)
rename dts/{framework => api}/params/types.py (97%)
diff --git a/doc/api/dts/framework.params.eal.rst b/doc/api/dts/api.params.eal.rst
similarity index 79%
rename from doc/api/dts/framework.params.eal.rst
rename to doc/api/dts/api.params.eal.rst
index 6999b00233..4531cb1fe1 100644
--- a/doc/api/dts/framework.params.eal.rst
+++ b/doc/api/dts/api.params.eal.rst
@@ -3,6 +3,6 @@
eal - EAL Parameters Modelling
==============================
-.. automodule:: framework.params.eal
+.. automodule:: api.params.eal
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.params.rst b/doc/api/dts/api.params.rst
similarity index 71%
rename from doc/api/dts/framework.params.rst
rename to doc/api/dts/api.params.rst
index d8c6af9667..3ea7f9215e 100644
--- a/doc/api/dts/framework.params.rst
+++ b/doc/api/dts/api.params.rst
@@ -3,7 +3,7 @@
params - Command Line Parameters Modelling
==========================================
-.. automodule:: framework.params
+.. automodule:: api.params
:members:
:show-inheritance:
@@ -11,5 +11,5 @@ params - Command Line Parameters Modelling
:hidden:
:maxdepth: 1
- framework.params.eal
- framework.params.types
+ api.params.eal
+ api.params.types
diff --git a/doc/api/dts/framework.params.types.rst b/doc/api/dts/api.params.types.rst
similarity index 80%
rename from doc/api/dts/framework.params.types.rst
rename to doc/api/dts/api.params.types.rst
index 6d609038be..4754b3a665 100644
--- a/doc/api/dts/framework.params.types.rst
+++ b/doc/api/dts/api.params.types.rst
@@ -3,6 +3,6 @@
params.types - Parameters Modelling Types
=========================================
-.. automodule:: framework.params.types
+.. automodule:: api.params.types
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 7c282bbba1..e89e782ac0 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -18,7 +18,7 @@ Packages
api
api.testbed_model
framework.remote_session
- framework.params
+ api.params
framework.config
Modules
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 1ba8e0d977..bbfe3622c2 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -27,7 +27,7 @@
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
- from framework.params.types import CryptoPmdParamsDict
+ from api.params.types import CryptoPmdParamsDict
from pathlib import PurePath
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index a88e70d45c..3420c2fe91 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.params import Params, Switch
+from api.params.eal import EalParams
from api.utils import StrEnum
-from framework.params import Params, Switch
-from framework.params.eal import EalParams
Silent = Literal[""]
diff --git a/dts/framework/params/__init__.py b/dts/api/params/__init__.py
similarity index 100%
rename from dts/framework/params/__init__.py
rename to dts/api/params/__init__.py
diff --git a/dts/framework/params/eal.py b/dts/api/params/eal.py
similarity index 97%
rename from dts/framework/params/eal.py
rename to dts/api/params/eal.py
index 86bfd3fcc6..64fa45ae12 100644
--- a/dts/framework/params/eal.py
+++ b/dts/api/params/eal.py
@@ -6,9 +6,9 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.params import Params, Switch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.virtual_device import VirtualDevice
-from framework.params import Params, Switch
if TYPE_CHECKING:
from api.testbed_model.port import Port
diff --git a/dts/framework/params/types.py b/dts/api/params/types.py
similarity index 97%
rename from dts/framework/params/types.py
rename to dts/api/params/types.py
index f2fa69f8b8..2c215cfe54 100644
--- a/dts/framework/params/types.py
+++ b/dts/api/params/types.py
@@ -12,8 +12,10 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
params = TestPmdParams(**kwargs)
"""
+from __future__ import annotations
+
from pathlib import PurePath
-from typing import TypedDict
+from typing import TYPE_CHECKING, TypedDict
from api.cryptodev.config import (
AeadAlgName,
@@ -56,7 +58,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TXRingParams,
TxUDPPortPair,
)
-from framework.params import Switch, YesNoSwitch
+
+if TYPE_CHECKING:
+ from api.params import Switch, YesNoSwitch
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9f47a15433..85ebe2ea65 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -14,6 +14,8 @@
testpmd.close()
"""
+from __future__ import annotations
+
import functools
import re
import time
@@ -21,6 +23,7 @@
from enum import Flag
from pathlib import PurePath
from typing import (
+ TYPE_CHECKING,
Any,
Callable,
ClassVar,
@@ -56,7 +59,9 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.params.types import TestPmdParamsDict
+
+if TYPE_CHECKING:
+ from api.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
from framework.settings import SETTINGS
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index 8b688834ee..96fe5e79fb 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,8 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
-from api.utils import StrEnum
-from framework.params import (
+from api.params import (
Params,
Switch,
YesNoSwitch,
@@ -24,7 +23,8 @@
modify_str,
str_from_flag_value,
)
-from framework.params.eal import EalParams
+from api.params.eal import EalParams
+from api.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 84db3974b1..537d937eca 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,9 +31,9 @@
from typing_extensions import Self
from api.context import get_ctx
+from api.params import Params
+from api.params.eal import EalParams
from api.testbed_model.node import Node
-from framework.params import Params
-from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 713a564d25..afdf7526d9 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -15,6 +15,7 @@
from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
@@ -32,7 +33,6 @@
RemoteDPDKTreeLocation,
)
from framework.logger import DTSLogger, get_dts_logger
-from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index b807f9bdae..61cc4687f3 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,8 +11,8 @@
from pathlib import PurePath
from api.context import get_ctx
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCoreList
-from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index ec539bad95..f7f0669eea 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -35,9 +35,9 @@
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from api.params import Params
from api.testbed_model.node import Node
from framework.logger import DTSLogger, get_dts_logger
-from framework.params import Params
from framework.settings import SETTINGS
P = ParamSpec("P")
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index fd5cef5268..ba67c9e1c6 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -35,6 +35,7 @@
match_all_packets,
send_packets_and_capture,
)
+from api.params import Params
from api.test import verify
from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
@@ -42,7 +43,6 @@
PacketFilteringConfig,
)
from api.testpmd import TestPmd
-from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index f1eb435759..24f2cebf17 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -21,11 +21,11 @@
requires_link_topology,
)
from api.packet import assess_performance_by_packet
+from api.params.types import TestPmdParamsDict
from api.test import verify, write_performance_json
from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
-from framework.params.types import TestPmdParamsDict
class Config(BaseConfig):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 7/7] dts: separate Linux session into interface and logic
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (5 preceding siblings ...)
2026-04-30 21:09 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
@ 2026-04-30 21:09 ` Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 21:09 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Separate Linux session into an interface for the API,
and a logical module in the framework.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/framework.linux_session.rst | 8 +
doc/api/dts/index.rst | 1 +
dts/api/testbed_model/linux_session.py | 372 ++----------------------
dts/api/testbed_model/node.py | 8 +-
dts/framework/linux_session.py | 366 +++++++++++++++++++++++
5 files changed, 403 insertions(+), 352 deletions(-)
create mode 100644 doc/api/dts/framework.linux_session.rst
create mode 100644 dts/framework/linux_session.py
diff --git a/doc/api/dts/framework.linux_session.rst b/doc/api/dts/framework.linux_session.rst
new file mode 100644
index 0000000000..9de2e1484d
--- /dev/null
+++ b/doc/api/dts/framework.linux_session.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+framework.linux\_session
+========================
+
+.. automodule:: framework.linux_session
+ :members:
+ :show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index e89e782ac0..0dbc18b75c 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -37,6 +37,7 @@ Modules
framework.parser
api.utils
api.exception
+ framework.linux_session
Indices and tables
diff --git a/dts/api/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
index 7307b2abe2..5bcbf1ce97 100644
--- a/dts/api/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -1,367 +1,41 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2023 PANTHEON.tech s.r.o.
# Copyright(c) 2023 University of New Hampshire
+"""Linux OS session interface.
-"""Linux OS translator.
-
-Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
-compliant with POSIX standards, so this module only implements the parts that aren't.
-This intermediate module implements the common parts of mostly POSIX compliant distributions.
+Extends the base :class:`~.os_session.OSSession` with methods specific to Linux nodes.
+The concrete implementation containing all backend logic lives in the framework package.
"""
-import json
-import re
-from collections.abc import Iterable
-from functools import cached_property
+from abc import ABC, abstractmethod
from pathlib import PurePath
-from typing import TypedDict
-
-from typing_extensions import NotRequired
-
-from api.exception import (
- ConfigurationError,
- InternalError,
- RemoteCommandExecutionError,
-)
-from api.testbed_model.port import PortInfo
-from api.utils import expand_range
-
-from .cpu import LogicalCore
-from .port import Port
-from .posix_session import PosixSession
-
-
-class LshwConfigurationOutput(TypedDict):
- """The relevant parts of ``lshw``'s ``configuration`` section."""
-
- #:
- driver: str
- #:
- link: str
-
-
-class LshwOutput(TypedDict):
- """A model of the relevant information from ``lshw``'s json output.
-
- Example:
- ::
-
- {
- ...
- "businfo" : "pci@0000:08:00.0",
- "logicalname" : "enp8s0",
- "version" : "00",
- "serial" : "52:54:00:59:e1:ac",
- ...
- "configuration" : {
- ...
- "link" : "yes",
- ...
- },
- ...
- """
-
- #:
- businfo: str
- #:
- logicalname: NotRequired[str]
- #:
- serial: NotRequired[str]
- #:
- configuration: LshwConfigurationOutput
-
-
-class LinuxSession(PosixSession):
- """The implementation of non-Posix compliant parts of Linux."""
-
- @staticmethod
- def _get_privileged_command(command: str) -> str:
- command = command.replace(r"'", r"\'")
- return f"sudo -- sh -c '{command}'"
- def get_remote_cpus(self) -> list[LogicalCore]:
- """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
- cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
- lcores = []
- for cpu_line in cpu_info.splitlines():
- lcore, core, socket, node = map(int, cpu_line.split(","))
- lcores.append(LogicalCore(lcore, core, socket, node))
- return lcores
-
- def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
- """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
- return dpdk_prefix
-
- def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
-
- Raises:
- ConfigurationError: If the given `hugepage_size` is not supported by the OS.
- """
- self._logger.info("Getting Hugepage information.")
- if (
- f"hugepages-{hugepage_size}kB"
- not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
- ):
- raise ConfigurationError("hugepage size not supported by operating system")
- hugepages_total = self._get_hugepages_total(hugepage_size)
- self._numa_nodes = self._get_numa_nodes()
-
- if force_first_numa or hugepages_total < number_of:
- # when forcing numa, we need to clear existing hugepages regardless
- # of size, so they can be moved to the first numa node
- self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
- else:
- self._logger.info("Hugepages already configured.")
- self._mount_huge_pages()
-
- def _get_hugepages_total(self, hugepage_size: int) -> int:
- hugepages_total = self.send_command(
- f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
- ).stdout
- return int(hugepages_total)
-
- def _get_numa_nodes(self) -> list[int]:
- try:
- numa_count = self.send_command(
- "cat /sys/devices/system/node/online", verify=True
- ).stdout
- numa_range = expand_range(numa_count)
- except RemoteCommandExecutionError:
- # the file doesn't exist, meaning the node doesn't support numa
- numa_range = []
- return numa_range
-
- def _mount_huge_pages(self) -> None:
- self._logger.info("Re-mounting Hugepages.")
- hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
- self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
- result = self.send_command(hugapge_fs_cmd)
- if result.stdout == "":
- remote_mount_path = "/mnt/huge"
- self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
- self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
-
- def _supports_numa(self) -> bool:
- # the system supports numa if self._numa_nodes is non-empty and there are more
- # than one numa node (in the latter case it may actually support numa, but
- # there's no reason to do any numa specific configuration)
- return len(self._numa_nodes) > 1
-
- def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
- self._logger.info("Configuring Hugepages.")
- hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
- if force_first_numa and self._supports_numa():
- # clear non-numa hugepages
- self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
- hugepage_config_path = (
- f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
- f"/hugepages-{size}kB/nr_hugepages"
- )
-
- self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
-
- def get_port_info(self, pci_address: str) -> PortInfo:
- """Overrides :meth:`~.os_session.OSSession.get_port_info`.
-
- Raises:
- ConfigurationError: If the port could not be found.
- """
- bus_info = f"pci@{pci_address}"
- port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
- if port is None:
- raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
- logical_name = port.get("logicalname", "")
- mac_address = port.get("serial", "")
+class LinuxSession(ABC):
+ """Abstract interface for Linux-specific OS session operations."""
- configuration = port.get("configuration", {})
- driver = configuration.get("driver", "")
- is_link_up = configuration.get("link", "down") == "up"
-
- return PortInfo(mac_address, logical_name, driver, is_link_up)
-
- def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
-
- The :attr:`~.devbind_script_path` property must be setup in order to call this method.
- """
- ports_pci_addrs = " ".join(port.pci for port in ports)
-
- self.send_command(
- f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
- privileged=True,
- verify=True,
- )
-
- del self._lshw_net_info
-
- def bring_up_link(self, ports: Iterable[Port]) -> None:
- """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
- for port in ports:
- self.send_command(
- f"ip link set dev {port.logical_name} up", privileged=True, verify=True
- )
-
- del self._lshw_net_info
-
- def set_interface_link_up(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
- self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
-
- def delete_interface(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
- self.send_command(f"ip link delete {name}", privileged=True)
-
- @cached_property
+ @property
+ @abstractmethod
def devbind_script_path(self) -> PurePath:
- """The path to the dpdk-devbind.py script on the node.
-
- Needs to be manually assigned first in order to be used.
+ """The path to the devbind script."""
- Raises:
- InternalError: If accessed before environment setup.
- """
- raise InternalError("Accessed devbind script path before setup.")
-
- def load_vfio(self, pf_port: Port) -> None:
- """Overrides :meth:`~os_session.OSSession,load_vfio`."""
- cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
- device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
- if device and device.group(1) in ["37c8", "0435", "19e2"]:
- self.send_command(
- "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
- privileged=True,
- )
- self.send_command(
- "modprobe -r vfio_virqfd; modprobe -r vfio",
- privileged=True,
- )
- self.send_command(
- "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
- )
- self.send_command(
- "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
- privileged=True,
- )
- else:
- self.send_command("modprobe vfio-pci")
- self.refresh_lshw()
+ @devbind_script_path.setter
+ @abstractmethod
+ def devbind_script_path(self, value: PurePath) -> None:
+ """Set the devbind script path after environment setup."""
- def create_crypto_vfs(self, pf_port: list[Port]) -> None:
- """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+ @abstractmethod
+ def set_interface_link_up(self, name: str) -> None:
+ """Set the link status of an interface to up.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ name: The name of the interface.
"""
- for port in pf_port:
- self.delete_crypto_vfs(port)
- for port in pf_port:
- sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
- )
- self.send_command(
- f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
- )
-
- self.refresh_lshw()
- def create_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+ @abstractmethod
+ def delete_interface(self, name: str) -> None:
+ """Delete a virtual interface.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ name: The name of the interface to delete.
"""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- if curr_num_vfs == 0:
- self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
- self.refresh_lshw()
-
- def delete_crypto_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
- self.send_command(
- f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
- privileged=True,
- )
- self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
-
- def delete_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if curr_num_vfs == 0:
- self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
- else:
- self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
-
- def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- f"readlink {sys_bus_path}/virtfn*",
- privileged=True,
- )
- return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
- return []
-
- def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
- + f"{sys_bus_path}/virtfn*/uevent",
- privileged=True,
- )
- return pci_addrs.stdout.splitlines()
- else:
- return []
-
- @cached_property
- def _lshw_net_info(self) -> list[LshwOutput]:
- output = self.send_command("lshw -quiet -json -C network", verify=True)
- return json.loads(output.stdout)
-
- def refresh_lshw(self) -> None:
- """Force refresh of cached lshw network info."""
- if "_lshw_net_info" in self.__dict__:
- del self.__dict__["_lshw_net_info"]
- _ = self._lshw_net_info
-
- def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
- if attr_value:
- setattr(port, attr_name, attr_value)
- self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
- else:
- self._logger.warning(
- f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
- )
-
- def configure_port_mtu(self, mtu: int, port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
- self.send_command(
- f"ip link set dev {port.logical_name} mtu {mtu}",
- privileged=True,
- verify=True,
- )
-
- def configure_ipv4_forwarding(self, enable: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
- state = 1 if enable else 0
- self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
diff --git a/dts/api/testbed_model/node.py b/dts/api/testbed_model/node.py
index 40dd7f0666..51abbd098b 100644
--- a/dts/api/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -15,17 +15,19 @@
from functools import cached_property
from pathlib import PurePath
-from typing import Literal, TypeAlias
+from typing import TYPE_CHECKING, Literal, TypeAlias
from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
+
+if TYPE_CHECKING:
+ from framework.linux_session import LinuxSession
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
-from .linux_session import LinuxSession
from .os_session import OSSession, OSSessionInfo
from .port import Port
@@ -211,7 +213,7 @@ def create_session(node_config: NodeConfiguration, name: str, logger: DTSLogger)
"""
match node_config.os:
case OS.linux:
- return LinuxSession(node_config, name, logger)
+ return "LinuxSession"(node_config, name, logger)
case _:
raise ConfigurationError(f"Unsupported OS {node_config.os}")
diff --git a/dts/framework/linux_session.py b/dts/framework/linux_session.py
new file mode 100644
index 0000000000..e5320b7fc4
--- /dev/null
+++ b/dts/framework/linux_session.py
@@ -0,0 +1,366 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2023 University of New Hampshire
+
+"""Linux OS translator.
+
+Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
+compliant with POSIX standards, so this module only implements the parts that aren't.
+This intermediate module implements the common parts of mostly POSIX compliant distributions.
+"""
+
+import json
+import re
+from collections.abc import Iterable
+from functools import cached_property
+from pathlib import PurePath
+from typing import TypedDict
+
+from typing_extensions import NotRequired
+
+from api.exception import (
+ ConfigurationError,
+ InternalError,
+ RemoteCommandExecutionError,
+)
+from api.testbed_model.cpu import LogicalCore
+from api.testbed_model.linux_session import LinuxSession as LinuxSessionBase
+from api.testbed_model.port import Port, PortInfo
+from api.testbed_model.posix_session import PosixSession
+from api.utils import expand_range
+
+
+class LshwConfigurationOutput(TypedDict):
+ """The relevant parts of ``lshw``'s ``configuration`` section."""
+
+ #:
+ driver: str
+ #:
+ link: str
+
+
+class LshwOutput(TypedDict):
+ """A model of the relevant information from ``lshw``'s json output.
+
+ Example:
+ ::
+
+ {
+ ...
+ "businfo" : "pci@0000:08:00.0",
+ "logicalname" : "enp8s0",
+ "version" : "00",
+ "serial" : "52:54:00:59:e1:ac",
+ ...
+ "configuration" : {
+ ...
+ "link" : "yes",
+ ...
+ },
+ ...
+ """
+
+ #:
+ businfo: str
+ #:
+ logicalname: NotRequired[str]
+ #:
+ serial: NotRequired[str]
+ #:
+ configuration: LshwConfigurationOutput
+
+
+class LinuxSession(PosixSession, LinuxSessionBase):
+ """The implementation of non-Posix compliant parts of Linux."""
+
+ @staticmethod
+ def _get_privileged_command(command: str) -> str:
+ command = command.replace(r"'", r"\'")
+ return f"sudo -- sh -c '{command}'"
+
+ def get_remote_cpus(self) -> list[LogicalCore]:
+ """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
+ cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
+ lcores = []
+ for cpu_line in cpu_info.splitlines():
+ lcore, core, socket, node = map(int, cpu_line.split(","))
+ lcores.append(LogicalCore(lcore, core, socket, node))
+ return lcores
+
+ def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
+ """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
+ return dpdk_prefix
+
+ def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
+
+ Raises:
+ ConfigurationError: If the given `hugepage_size` is not supported by the OS.
+ """
+ self._logger.info("Getting Hugepage information.")
+ if (
+ f"hugepages-{hugepage_size}kB"
+ not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
+ ):
+ raise ConfigurationError("hugepage size not supported by operating system")
+ hugepages_total = self._get_hugepages_total(hugepage_size)
+ self._numa_nodes = self._get_numa_nodes()
+
+ if force_first_numa or hugepages_total < number_of:
+ # when forcing numa, we need to clear existing hugepages regardless
+ # of size, so they can be moved to the first numa node
+ self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
+ else:
+ self._logger.info("Hugepages already configured.")
+ self._mount_huge_pages()
+
+ def _get_hugepages_total(self, hugepage_size: int) -> int:
+ hugepages_total = self.send_command(
+ f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
+ ).stdout
+ return int(hugepages_total)
+
+ def _get_numa_nodes(self) -> list[int]:
+ try:
+ numa_count = self.send_command(
+ "cat /sys/devices/system/node/online", verify=True
+ ).stdout
+ numa_range = expand_range(numa_count)
+ except RemoteCommandExecutionError:
+ # the file doesn't exist, meaning the node doesn't support numa
+ numa_range = []
+ return numa_range
+
+ def _mount_huge_pages(self) -> None:
+ self._logger.info("Re-mounting Hugepages.")
+ hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
+ self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
+ result = self.send_command(hugapge_fs_cmd)
+ if result.stdout == "":
+ remote_mount_path = "/mnt/huge"
+ self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
+ self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
+
+ def _supports_numa(self) -> bool:
+ # the system supports numa if self._numa_nodes is non-empty and there are more
+ # than one numa node (in the latter case it may actually support numa, but
+ # there's no reason to do any numa specific configuration)
+ return len(self._numa_nodes) > 1
+
+ def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
+ self._logger.info("Configuring Hugepages.")
+ hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
+ if force_first_numa and self._supports_numa():
+ # clear non-numa hugepages
+ self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
+ hugepage_config_path = (
+ f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
+ f"/hugepages-{size}kB/nr_hugepages"
+ )
+
+ self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
+
+ def get_port_info(self, pci_address: str) -> PortInfo:
+ """Overrides :meth:`~.os_session.OSSession.get_port_info`.
+
+ Raises:
+ ConfigurationError: If the port could not be found.
+ """
+ bus_info = f"pci@{pci_address}"
+ port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
+ if port is None:
+ raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
+
+ logical_name = port.get("logicalname", "")
+ mac_address = port.get("serial", "")
+
+ configuration = port.get("configuration", {})
+ driver = configuration.get("driver", "")
+ is_link_up = configuration.get("link", "down") == "up"
+
+ return PortInfo(mac_address, logical_name, driver, is_link_up)
+
+ def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
+
+ The :attr:`~.devbind_script_path` property must be setup in order to call this method.
+ """
+ ports_pci_addrs = " ".join(port.pci for port in ports)
+
+ self.send_command(
+ f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
+ privileged=True,
+ verify=True,
+ )
+
+ del self._lshw_net_info
+
+ def bring_up_link(self, ports: Iterable[Port]) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
+ for port in ports:
+ self.send_command(
+ f"ip link set dev {port.logical_name} up", privileged=True, verify=True
+ )
+
+ del self._lshw_net_info
+
+ def set_interface_link_up(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
+ self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
+
+ def delete_interface(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
+ self.send_command(f"ip link delete {name}", privileged=True)
+
+ @cached_property
+ def devbind_script_path(self) -> PurePath:
+ """The path to the dpdk-devbind.py script on the node.
+
+ Needs to be manually assigned first in order to be used.
+
+ Raises:
+ InternalError: If accessed before environment setup.
+ """
+ raise InternalError("Accessed devbind script path before setup.")
+
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~os_session.OSSession,load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self.refresh_lshw()
+
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+
+ self.refresh_lshw()
+
+ def create_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ if curr_num_vfs == 0:
+ self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ self.refresh_lshw()
+
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
+ def delete_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if curr_num_vfs == 0:
+ self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
+ else:
+ self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
+ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
+ + f"{sys_bus_path}/virtfn*/uevent",
+ privileged=True,
+ )
+ return pci_addrs.stdout.splitlines()
+ else:
+ return []
+
+ @cached_property
+ def _lshw_net_info(self) -> list[LshwOutput]:
+ output = self.send_command("lshw -quiet -json -C network", verify=True)
+ return json.loads(output.stdout)
+
+ def refresh_lshw(self) -> None:
+ """Force refresh of cached lshw network info."""
+ if "_lshw_net_info" in self.__dict__:
+ del self.__dict__["_lshw_net_info"]
+ _ = self._lshw_net_info
+
+ def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
+ if attr_value:
+ setattr(port, attr_name, attr_value)
+ self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
+ else:
+ self._logger.warning(
+ f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
+ )
+
+ def configure_port_mtu(self, mtu: int, port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
+ self.send_command(
+ f"ip link set dev {port.logical_name} mtu {mtu}",
+ privileged=True,
+ verify=True,
+ )
+
+ def configure_ipv4_forwarding(self, enable: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
+ state = 1 if enable else 0
+ self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 0/7] dts: move test suite imports from framework to API
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
` (8 preceding siblings ...)
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
@ 2026-04-30 22:06 ` Dean Marx
2026-04-30 22:06 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
` (6 more replies)
9 siblings, 7 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 22:06 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
This series moves various modules from the framework directory
to the API based on which are being imported by test suites.
These include:
- test_suite
- testbed_model
- exception
- utils
- context
- params
This eliminates all test suite framework imports except for
the remote_session imports in packet_capture, as well
as the settings/config imports in smoke_tests. I believe these
imports, and what to do with them, should be a topic of discussion
in future DTS meetings, as I don't believe they should reside in the
API, even if they are being imported in test suites.
In addition to these changes, I've split the linux_session module
into an interface in api/testbed_model/linux_session and an
implementation in framework/linux_session. This way, users
can still import linux session if necessary, without exposing
the backend implementation.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
v2:
* Integrated rst updates with corresponding commits for build testing
v3:
* Fixed commit ordering to resolve import inconsistency
* Updated Linux Session interface with test suite methods
v4:
* Added missing SPDX tag to framework.linux_session.rst
Dean Marx (7):
dts: move exception module from framework to API
dts: move utils from framework to API
dts: move context from framework to API
dts: move testbed model from framework to API
dts: move test suite module from framework to API
dts: move params directory from framework to API
dts: separate Linux session into interface and logic
...{framework.context.rst => api.context.rst} | 2 +-
...mework.exception.rst => api.exception.rst} | 2 +-
...work.params.eal.rst => api.params.eal.rst} | 2 +-
.../{framework.params.rst => api.params.rst} | 6 +--
....params.types.rst => api.params.types.rst} | 2 +-
...work.test_suite.rst => api.test_suite.rst} | 2 +-
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 ++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
.../{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/framework.linux_session.rst | 8 ++++
doc/api/dts/framework.testbed_model.rst | 28 -------------
...mework.testbed_model.traffic_generator.rst | 16 --------
doc/api/dts/index.rst | 13 +++---
dts/api/artifact.py | 6 +--
dts/api/capabilities.py | 14 +++----
dts/{framework => api}/context.py | 14 +++----
dts/api/cryptodev/__init__.py | 6 +--
dts/api/cryptodev/config.py | 6 +--
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 18 ++++----
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 8 ++--
dts/{framework => api}/params/types.py | 14 ++++---
dts/api/test.py | 4 +-
dts/{framework => api}/test_suite.py | 15 ++++---
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 18 ++++----
dts/{framework => api}/testbed_model/cpu.py | 4 +-
dts/api/testbed_model/linux_session.py | 41 +++++++++++++++++++
dts/{framework => api}/testbed_model/node.py | 10 +++--
.../testbed_model/os_session.py | 8 ++--
dts/{framework => api}/testbed_model/port.py | 2 +-
.../testbed_model/posix_session.py | 6 +--
.../testbed_model/topology.py | 20 ++++-----
.../traffic_generator/__init__.py | 4 +-
.../capturing_traffic_generator.py | 4 +-
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 14 +++----
.../traffic_generator/traffic_generator.py | 8 ++--
.../testbed_model/traffic_generator/trex.py | 16 ++++----
.../testbed_model/virtual_device.py | 0
dts/api/testpmd/__init__.py | 11 +++--
dts/api/testpmd/config.py | 6 +--
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 2 +-
dts/framework/config/__init__.py | 6 +--
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 12 +++---
.../{testbed_model => }/linux_session.py | 15 ++++---
dts/framework/parser.py | 2 +-
dts/framework/remote_session/blocking_app.py | 8 ++--
dts/framework/remote_session/dpdk.py | 16 ++++----
dts/framework/remote_session/dpdk_shell.py | 6 +--
.../interactive_remote_session.py | 2 +-
.../remote_session/interactive_shell.py | 8 ++--
.../remote_session/remote_session.py | 4 +-
dts/framework/runner.py | 4 +-
dts/framework/test_result.py | 5 +--
dts/framework/test_run.py | 24 +++++------
dts/tests/TestSuite_blocklist.py | 4 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 8 ++--
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 4 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 8 ++--
dts/tests/TestSuite_mac_filter.py | 4 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 12 +++---
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
dts/tests/TestSuite_pmd_rss.py | 6 +--
dts/tests/TestSuite_port_control.py | 2 +-
...stSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 12 +++---
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
.../TestSuite_single_core_forward_perf.py | 4 +-
dts/tests/TestSuite_smoke_tests.py | 6 +--
dts/tests/TestSuite_softnic.py | 6 +--
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 8 ++--
dts/tests/TestSuite_vlan.py | 2 +-
98 files changed, 358 insertions(+), 300 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
create mode 100644 doc/api/dts/framework.linux_session.rst
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/context.py (90%)
rename dts/{framework => api}/exception.py (100%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (89%)
rename dts/{framework => api}/params/types.py (94%)
rename dts/{framework => api}/test_suite.py (98%)
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (98%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
create mode 100644 dts/api/testbed_model/linux_session.py
rename dts/{framework => api}/testbed_model/node.py (97%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (98%)
rename dts/{framework => api}/testbed_model/posix_session.py (99%)
rename dts/{framework => api}/testbed_model/topology.py (96%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (95%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (91%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (96%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
rename dts/{framework => api}/utils.py (99%)
rename dts/framework/{testbed_model => }/linux_session.py (97%)
--
2.52.0
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v4 1/7] dts: move exception module from framework to API
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
@ 2026-04-30 22:06 ` Dean Marx
2026-04-30 22:06 ` [PATCH v4 2/7] dts: move utils " Dean Marx
` (5 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 22:06 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites currently import the exception module
from the framework in order to catch certain errors during
test execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.exception.rst => api.exception.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/{framework => api}/exception.py | 0
dts/api/packet.py | 2 +-
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/config/__init__.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/context.py | 2 +-
dts/framework/parser.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
.../remote_session/interactive_remote_session.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 4 ++--
dts/framework/remote_session/remote_session.py | 4 ++--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 3 +--
dts/framework/test_run.py | 4 ++--
dts/framework/test_suite.py | 2 +-
dts/framework/testbed_model/capability.py | 2 +-
dts/framework/testbed_model/linux_session.py | 2 +-
dts/framework/testbed_model/node.py | 2 +-
dts/framework/testbed_model/port.py | 2 +-
dts/framework/testbed_model/posix_session.py | 2 +-
dts/framework/testbed_model/topology.py | 2 +-
.../testbed_model/traffic_generator/__init__.py | 2 +-
dts/framework/testbed_model/traffic_generator/scapy.py | 2 +-
dts/framework/utils.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
| 2 +-
dts/tests/TestSuite_rte_flow.py | 10 +++++-----
34 files changed, 40 insertions(+), 41 deletions(-)
rename doc/api/dts/{framework.exception.rst => api.exception.rst} (77%)
rename dts/{framework => api}/exception.py (100%)
diff --git a/doc/api/dts/framework.exception.rst b/doc/api/dts/api.exception.rst
similarity index 77%
rename from doc/api/dts/framework.exception.rst
rename to doc/api/dts/api.exception.rst
index efb47dc5ae..8e6bff5ee7 100644
--- a/doc/api/dts/framework.exception.rst
+++ b/doc/api/dts/api.exception.rst
@@ -3,6 +3,6 @@
exception - Exceptions
======================
-.. automodule:: framework.exception
+.. automodule:: api.exception
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index c719297c11..01f630e7cd 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -36,7 +36,7 @@ Modules
framework.logger
framework.parser
framework.utils
- framework.exception
+ api.exception
Indices and tables
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 24a2b05063..7d04c7ab49 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -47,7 +47,7 @@
from paramiko import SFTPClient, SFTPFile
from typing_extensions import Buffer
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
from framework.testbed_model.node import Node, NodeIdentifier, get_node
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index a4fafc3713..c6a220dced 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -22,8 +22,8 @@
ThroughputResults,
VerifyResults,
)
+from api.exception import RemoteCommandExecutionError, SkippedTestException
from framework.context import get_ctx
-from framework.exception import RemoteCommandExecutionError, SkippedTestException
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/framework/exception.py b/dts/api/exception.py
similarity index 100%
rename from dts/framework/exception.py
rename to dts/api/exception.py
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 094a1b7a9d..cabb39a8dd 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,9 +27,9 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.exception import InternalError
from api.test import fail, log_debug
from framework.context import get_ctx
-from framework.exception import InternalError
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index e17babe0ca..9cad9a9495 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.context import get_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..9498d723d5 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
ChecksumOffloadOptions,
@@ -55,7 +56,6 @@
VLANOffloadFlag,
)
from framework.context import get_ctx
-from framework.exception import InteractiveCommandExecutionError, InternalError
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index d2f0138e4a..566dc7c4a2 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -35,7 +35,7 @@
from pydantic import Field, TypeAdapter, ValidationError, model_validator
from typing_extensions import Self
-from framework.exception import ConfigurationError
+from api.exception import ConfigurationError
from .common import FrozenModel, ValidationContext
from .node import NodeConfiguration
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 76e24d1785..62aaba033a 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -27,7 +27,7 @@
)
from typing_extensions import TYPE_CHECKING, Self
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/context.py b/dts/framework/context.py
index 8f1021dc96..7e61c85998 100644
--- a/dts/framework/context.py
+++ b/dts/framework/context.py
@@ -8,7 +8,7 @@
from dataclasses import MISSING, dataclass, field, fields
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
-from framework.exception import InternalError
+from api.exception import InternalError
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
diff --git a/dts/framework/parser.py b/dts/framework/parser.py
index 3075c36857..ebf470ad30 100644
--- a/dts/framework/parser.py
+++ b/dts/framework/parser.py
@@ -15,7 +15,7 @@
from typing_extensions import Self
-from framework.exception import InternalError
+from api.exception import InternalError
class ParserFn(TypedDict):
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index c3575cfcaf..d803a9e4bd 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,6 +13,7 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.exception import ConfigurationError, RemoteFileNotFoundError
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -25,7 +26,6 @@
RemoteDPDKTreeLocation,
)
from framework.context import get_ctx
-from framework.exception import ConfigurationError, RemoteFileNotFoundError
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/interactive_remote_session.py b/dts/framework/remote_session/interactive_remote_session.py
index c8156b4345..04f45e0df8 100644
--- a/dts/framework/remote_session/interactive_remote_session.py
+++ b/dts/framework/remote_session/interactive_remote_session.py
@@ -15,8 +15,8 @@
SSHException,
)
+from api.exception import SSHConnectionError
from framework.config.node import NodeConfiguration
-from framework.exception import SSHConnectionError
from framework.logger import DTSLogger
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index a65cbce209..fdd074be3a 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,12 +29,12 @@
from paramiko import Channel, channel
from typing_extensions import Self
-from framework.context import get_ctx
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from framework.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/remote_session/remote_session.py b/dts/framework/remote_session/remote_session.py
index 158325bb7f..f49966070f 100644
--- a/dts/framework/remote_session/remote_session.py
+++ b/dts/framework/remote_session/remote_session.py
@@ -24,13 +24,13 @@
SSHException,
)
-from framework.config.node import NodeConfiguration
-from framework.exception import (
+from api.exception import (
RemoteCommandExecutionError,
SSHConnectionError,
SSHSessionDeadError,
SSHTimeoutError,
)
+from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.settings import SETTINGS
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index 6ea4749ff4..a0d8039a04 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -12,8 +12,8 @@
import sys
import textwrap
+from api.exception import ConfigurationError
from framework.config.common import ValidationContext
-from framework.exception import ConfigurationError
from framework.test_run import TestRun
from framework.testbed_model.node import Node
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 21faa55dc1..3cecb928ca 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -35,12 +35,11 @@
)
from typing_extensions import OrderedDict
+from api.exception import DTSError, ErrorSeverity, InternalError
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
from framework.testbed_model.os_session import OSSessionInfo
-from .exception import DTSError, ErrorSeverity, InternalError
-
class Result(IntEnum):
"""The possible states that a setup, a teardown or a test case may end up in."""
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..bbaf4f1fdf 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,9 +106,9 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.config.test_run import TestRunConfiguration
from framework.context import Context, init_ctx
-from framework.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
@@ -136,7 +136,7 @@ class TestRun:
If an error occurs, the current stage is aborted, the error is recorded, everything in
the inner stages is marked as blocked and the run continues in the next iteration
of the same stage. The return code is the highest `severity` of all
- :class:`~.framework.exception.DTSError`\s.
+ :class:`~.api.exception.DTSError`\s.
Example:
An error occurs in a test suite setup. The current test suite is aborted,
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 69ce26040a..e06fdd28b9 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -29,11 +29,11 @@
from scapy.packet import Packet
from typing_extensions import Self
+from api.exception import ConfigurationError, InternalError
from framework.config.common import FrozenModel
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
-from .exception import ConfigurationError, InternalError
from .logger import DTSLogger, get_dts_logger
from .utils import to_pascal_case
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 960370fc72..001b65994c 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -64,7 +64,7 @@ def test_scatter_mbuf_2048(self):
from typing_extensions import Self
from api.capabilities import LinkTopology, NicCapability
-from framework.exception import ConfigurationError, InternalError, SkippedTestException
+from api.exception import ConfigurationError, InternalError, SkippedTestException
from framework.logger import get_dts_logger
from framework.testbed_model.node import Node
from framework.testbed_model.port import DriverKind
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index ee943462c2..88b6da1ae6 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -18,7 +18,7 @@
from typing_extensions import NotRequired
-from framework.exception import (
+from api.exception import (
ConfigurationError,
InternalError,
RemoteCommandExecutionError,
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 67a96ef4e5..4f42bf6aeb 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -17,11 +17,11 @@
from pathlib import PurePath
from typing import Literal, TypeAlias
+from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
-from framework.exception import ConfigurationError, InternalError
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
diff --git a/dts/framework/testbed_model/port.py b/dts/framework/testbed_model/port.py
index d81bc4cda0..aea3e59c25 100644
--- a/dts/framework/testbed_model/port.py
+++ b/dts/framework/testbed_model/port.py
@@ -12,8 +12,8 @@
from functools import cached_property
from typing import TYPE_CHECKING, Any, Final, Literal, NamedTuple
+from api.exception import InternalError
from framework.config.node import PortConfig
-from framework.exception import InternalError
if TYPE_CHECKING:
from .node import Node
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/framework/testbed_model/posix_session.py
index dec952685a..db2c3c0c40 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -16,7 +16,7 @@
from collections.abc import Iterable
from pathlib import Path, PurePath, PurePosixPath
-from framework.exception import DPDKBuildError, RemoteCommandExecutionError
+from api.exception import DPDKBuildError, RemoteCommandExecutionError
from framework.settings import SETTINGS
from framework.utils import (
MesonArgs,
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 34862c4d2e..805a762c19 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -18,7 +18,7 @@
from typing_extensions import Self
from api.capabilities import LinkTopology
-from framework.exception import ConfigurationError, InternalError
+from api.exception import ConfigurationError, InternalError
from framework.testbed_model.linux_session import LinuxSession
from framework.testbed_model.node import Node, NodeIdentifier
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/framework/testbed_model/traffic_generator/__init__.py
index fca251f534..324b5e88f3 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/framework/testbed_model/traffic_generator/__init__.py
@@ -14,12 +14,12 @@
and a capturing traffic generator is required.
"""
+from api.exception import ConfigurationError
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.exception import ConfigurationError
from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/framework/testbed_model/traffic_generator/scapy.py
index c6e9006205..e983443548 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/framework/testbed_model/traffic_generator/scapy.py
@@ -25,9 +25,9 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet
+from api.exception import InteractiveSSHSessionDeadError, InternalError
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
-from framework.exception import InteractiveSSHSessionDeadError, InternalError
from framework.remote_session.python_shell import PythonShell
from framework.testbed_model.node import Node
from framework.testbed_model.port import Port
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index 9917ffbfaa..28e344871a 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -26,7 +26,7 @@
from scapy.layers.inet import IP, TCP, UDP, Ether
from scapy.packet import Packet
-from .exception import InternalError
+from api.exception import InternalError
REGEX_FOR_PCI_ADDRESS: str = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}"
_REGEX_FOR_COLON_OR_HYPHEN_SEP_MAC: str = r"(?:[\da-fA-F]{2}[:-]){5}[\da-fA-F]{2}"
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index af0a5680ab..f36b48a153 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -29,9 +29,9 @@
from api.cryptodev.types import (
CryptodevResults,
)
+from api.exception import SkippedTestException
from api.test import verify
from framework.context import get_ctx
-from framework.exception import SkippedTestException
from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index 5ac85bee7d..b62efa2b42 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -35,11 +35,11 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import TestSuite, func_test
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index a7e24b37d5..b44822d31c 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -23,10 +23,10 @@
NicCapability,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
from api.testpmd import TestPmd
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import TestSuite, func_test
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index f6adf262c3..1e5a6860be 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -20,6 +20,7 @@
requires_link_topology,
requires_nic_capability,
)
+from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
from api.testpmd import TestPmd
@@ -29,7 +30,6 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
-from framework.exception import InteractiveCommandExecutionError
from framework.test_suite import BaseConfig, TestSuite, func_test
from framework.utils import StrEnum
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 6255e4c36d..7e50a075ac 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -21,15 +21,15 @@
from scapy.packet import Packet, Raw
from api.capabilities import NicCapability, requires_nic_capability
-from api.packet import send_packet_and_capture
-from api.test import fail, log, verify
-from api.testpmd import TestPmd
-from api.testpmd.types import FlowRule
-from framework.exception import (
+from api.exception import (
InteractiveCommandExecutionError,
SkippedTestException,
TestCaseVerifyError,
)
+from api.packet import send_packet_and_capture
+from api.test import fail, log, verify
+from api.testpmd import TestPmd
+from api.testpmd.types import FlowRule
from framework.test_suite import TestSuite, func_test
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 2/7] dts: move utils from framework to API
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-30 22:06 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
@ 2026-04-30 22:06 ` Dean Marx
2026-04-30 22:06 ` [PATCH v4 3/7] dts: move context " Dean Marx
` (4 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 22:06 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The utils module is used to generate a set of random
packets in certain test suites. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/{framework.utils.rst => api.utils.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/config.py | 2 +-
dts/api/packet.py | 2 +-
dts/api/testpmd/config.py | 2 +-
dts/api/testpmd/types.py | 2 +-
dts/{framework => api}/utils.py | 0
dts/framework/config/node.py | 2 +-
dts/framework/config/test_run.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/test_suite.py | 2 +-
dts/framework/testbed_model/cpu.py | 2 +-
dts/framework/testbed_model/linux_session.py | 2 +-
dts/framework/testbed_model/os_session.py | 2 +-
dts/framework/testbed_model/posix_session.py | 4 ++--
.../traffic_generator/capturing_traffic_generator.py | 2 +-
.../testbed_model/traffic_generator/traffic_generator.py | 2 +-
dts/framework/testbed_model/traffic_generator/trex.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
| 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
22 files changed, 22 insertions(+), 22 deletions(-)
rename doc/api/dts/{framework.utils.rst => api.utils.rst} (80%)
rename dts/{framework => api}/utils.py (100%)
diff --git a/doc/api/dts/framework.utils.rst b/doc/api/dts/api.utils.rst
similarity index 80%
rename from doc/api/dts/framework.utils.rst
rename to doc/api/dts/api.utils.rst
index cc06d4c3c3..0b4baff1b5 100644
--- a/doc/api/dts/framework.utils.rst
+++ b/doc/api/dts/api.utils.rst
@@ -3,6 +3,6 @@
utils - Various Utilities
=========================
-.. automodule:: framework.utils
+.. automodule:: api.utils
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 01f630e7cd..783270f6e9 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -35,7 +35,7 @@ Modules
framework.context
framework.logger
framework.parser
- framework.utils
+ api.utils
api.exception
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index 69ff7aa59a..a88e70d45c 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.utils import StrEnum
from framework.params import Params, Switch
from framework.params.eal import EalParams
-from framework.utils import StrEnum
Silent = Literal[""]
diff --git a/dts/api/packet.py b/dts/api/packet.py
index cabb39a8dd..b7a9bb28bf 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -29,6 +29,7 @@
from api.exception import InternalError
from api.test import fail, log_debug
+from api.utils import get_packet_summaries
from framework.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -36,7 +37,6 @@
from framework.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
-from framework.utils import get_packet_summaries
def send_packet_and_capture(
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index e71a3e1ef0..8b688834ee 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,6 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
+from api.utils import StrEnum
from framework.params import (
Params,
Switch,
@@ -24,7 +25,6 @@
str_from_flag_value,
)
from framework.params.eal import EalParams
-from framework.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..5c847b4bd6 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -15,8 +15,8 @@
from typing_extensions import Self
+from api.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
from framework.parser import ParserFn, TextParser
-from framework.utils import REGEX_FOR_MAC_ADDRESS, StrEnum
RxTxLiteralSwitch = Literal["rx", "tx"]
diff --git a/dts/framework/utils.py b/dts/api/utils.py
similarity index 100%
rename from dts/framework/utils.py
rename to dts/api/utils.py
diff --git a/dts/framework/config/node.py b/dts/framework/config/node.py
index 792290f11f..28f23389a7 100644
--- a/dts/framework/config/node.py
+++ b/dts/framework/config/node.py
@@ -14,7 +14,7 @@
from pydantic import Field, model_validator
from typing_extensions import Self
-from framework.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
+from api.utils import REGEX_FOR_IDENTIFIER, REGEX_FOR_PCI_ADDRESS, StrEnum
from .common import FrozenModel
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 62aaba033a..977067f42a 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -28,7 +28,7 @@
from typing_extensions import TYPE_CHECKING, Self
from api.exception import InternalError
-from framework.utils import REGEX_FOR_PORT_LINK, StrEnum
+from api.utils import REGEX_FOR_PORT_LINK, StrEnum
from .common import FrozenModel, load_fields_from_settings
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index d803a9e4bd..69b47b823b 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -14,6 +14,7 @@
from typing import ClassVar, Final
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
DPDKBuildOptionsConfiguration,
@@ -33,7 +34,6 @@
from framework.testbed_model.node import Node
from framework.testbed_model.os_session import OSSession
from framework.testbed_model.virtual_device import VirtualDevice
-from framework.utils import MesonArgs, TarCompressionFormat
@dataclass(slots=True, frozen=True)
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index e06fdd28b9..426c98fdf6 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -30,12 +30,12 @@
from typing_extensions import Self
from api.exception import ConfigurationError, InternalError
+from api.utils import to_pascal_case
from framework.config.common import FrozenModel
from framework.testbed_model.capability import TestProtocol
from framework.testbed_model.topology import Topology
from .logger import DTSLogger, get_dts_logger
-from .utils import to_pascal_case
if TYPE_CHECKING:
from framework.context import Context
diff --git a/dts/framework/testbed_model/cpu.py b/dts/framework/testbed_model/cpu.py
index 6e2ecca080..52ef196f84 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/framework/testbed_model/cpu.py
@@ -24,7 +24,7 @@
from dataclasses import dataclass
from enum import auto, unique
-from framework.utils import StrEnum, expand_range
+from api.utils import StrEnum, expand_range
@unique
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 88b6da1ae6..69b0923744 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -23,8 +23,8 @@
InternalError,
RemoteCommandExecutionError,
)
+from api.utils import expand_range
from framework.testbed_model.port import PortInfo
-from framework.utils import expand_range
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index 2c267afed1..7bb339fab2 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -29,12 +29,12 @@
from enum import Flag, auto
from pathlib import Path, PurePath, PurePosixPath
+from api.utils import MesonArgs, TarCompressionFormat
from framework.config.node import NodeConfiguration
from framework.logger import DTSLogger
from framework.remote_session.interactive_remote_session import InteractiveRemoteSession
from framework.remote_session.remote_session import CommandResult, RemoteSession
from framework.settings import SETTINGS
-from framework.utils import MesonArgs, TarCompressionFormat
from .cpu import Architecture, LogicalCore
from .port import Port, PortInfo
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/framework/testbed_model/posix_session.py
index db2c3c0c40..61c634dad1 100644
--- a/dts/framework/testbed_model/posix_session.py
+++ b/dts/framework/testbed_model/posix_session.py
@@ -17,14 +17,14 @@
from pathlib import Path, PurePath, PurePosixPath
from api.exception import DPDKBuildError, RemoteCommandExecutionError
-from framework.settings import SETTINGS
-from framework.utils import (
+from api.utils import (
MesonArgs,
TarCompressionFormat,
convert_to_list_of_string,
create_tarball,
extract_tarball,
)
+from framework.settings import SETTINGS
from .cpu import Architecture
from .os_session import FilePermissions, OSSession, OSSessionInfo
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
index 7655751d7e..2804d64990 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
+from api.utils import get_packet_summaries
from framework.testbed_model.port import Port
-from framework.utils import get_packet_summaries
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/framework/testbed_model/traffic_generator/traffic_generator.py
index cdda5a7c08..fedce77fdf 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/framework/testbed_model/traffic_generator/traffic_generator.py
@@ -25,7 +25,7 @@ class TrafficGenerator(ABC):
Exposes the common public methods of all traffic generators and defines private methods
that must implement the traffic generation logic in subclasses. This class also extends from
- :class:`framework.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
+ :class:`api.utils.MultiInheritanceBaseClass` to allow subclasses the ability to inherit
from multiple classes to fulfil the traffic generating functionality without breaking
single inheritance.
"""
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/framework/testbed_model/traffic_generator/trex.py
index 22cd20dea9..2064703fcc 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/framework/testbed_model/traffic_generator/trex.py
@@ -11,6 +11,7 @@
from scapy.packet import Packet
+from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
@@ -23,7 +24,6 @@
PerformanceTrafficGenerator,
PerformanceTrafficStats,
)
-from framework.utils import StrEnum
@dataclass(slots=True)
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 596b892730..1e99b82b8c 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -20,10 +20,10 @@
)
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
+from api.utils import generate_random_packets
from framework.context import filter_cores
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 1e5a6860be..4df273e3e1 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -30,8 +30,8 @@
RSSOffloadTypesFlag,
TestPmdVerbosePacket,
)
+from api.utils import StrEnum
from framework.test_suite import BaseConfig, TestSuite, func_test
-from framework.utils import StrEnum
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 271ad4301c..fce83604a6 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -20,11 +20,11 @@
)
from api.test import verify
from api.testpmd import TestPmd
+from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.linux_session import LinuxSession
-from framework.utils import REGEX_FOR_PCI_ADDRESS
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index fa91f7ee2f..c57a12c932 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -20,9 +20,9 @@
)
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
+from api.utils import generate_random_packets
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.virtual_device import VirtualDevice
-from framework.utils import generate_random_packets
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 3/7] dts: move context from framework to API
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-30 22:06 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
2026-04-30 22:06 ` [PATCH v4 2/7] dts: move utils " Dean Marx
@ 2026-04-30 22:06 ` Dean Marx
2026-04-30 22:06 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
` (3 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 22:06 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
A couple test suites import and get the run context
during execution. Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.context.rst => api.context.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/{framework => api}/context.py | 0
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 4 ++--
dts/api/test.py | 2 +-
dts/api/testpmd/__init__.py | 2 +-
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/framework/test_run.py | 2 +-
dts/framework/test_suite.py | 4 ++--
dts/framework/testbed_model/node.py | 2 +-
dts/framework/testbed_model/topology.py | 14 +++++++-------
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
18 files changed, 25 insertions(+), 25 deletions(-)
rename doc/api/dts/{framework.context.rst => api.context.rst} (80%)
rename dts/{framework => api}/context.py (100%)
diff --git a/doc/api/dts/framework.context.rst b/doc/api/dts/api.context.rst
similarity index 80%
rename from doc/api/dts/framework.context.rst
rename to doc/api/dts/api.context.rst
index 925c160360..65b4ab9a47 100644
--- a/doc/api/dts/framework.context.rst
+++ b/doc/api/dts/api.context.rst
@@ -3,6 +3,6 @@
context - DTS execution context
===============================
-.. automodule:: framework.context
+.. automodule:: api.context
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 783270f6e9..98269d6e80 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -32,7 +32,7 @@ Modules
framework.test_suite
framework.test_result
framework.settings
- framework.context
+ api.context
framework.logger
framework.parser
api.utils
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index 7d04c7ab49..f3dd07de56 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -86,7 +86,7 @@ def make_file_path(
path /= custom_path
else:
- from framework.context import get_ctx
+ from api.context import get_ctx
try:
ctx = get_ctx()
diff --git a/dts/framework/context.py b/dts/api/context.py
similarity index 100%
rename from dts/framework/context.py
rename to dts/api/context.py
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index c6a220dced..67dcb02130 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -23,7 +23,7 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index b7a9bb28bf..59f26da833 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -30,7 +30,7 @@
from api.exception import InternalError
from api.test import fail, log_debug
from api.utils import get_packet_summaries
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
@@ -82,7 +82,7 @@ def send_packets_and_capture(
Returns:
A list of received packets.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index 9cad9a9495..a1f2326075 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -11,7 +11,7 @@
from api.artifact import Artifact
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9498d723d5..a528663c21 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -55,7 +55,7 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index c3b02dcc62..07db6dfeb0 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -30,7 +30,7 @@
from typing_extensions import Self
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 69b47b823b..9f7cd60dfe 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -26,7 +26,7 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index 269c2cada4..a8f169787c 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -10,7 +10,7 @@
from abc import ABC, abstractmethod
from pathlib import PurePath
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index fdd074be3a..d138727c85 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -34,7 +34,7 @@
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index bbaf4f1fdf..605a916ebb 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -108,7 +108,7 @@
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.config.test_run import TestRunConfiguration
-from framework.context import Context, init_ctx
+from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 426c98fdf6..6066f7a77a 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -38,7 +38,7 @@
from .logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
- from framework.context import Context
+ from api.context import Context
class BaseConfig(FrozenModel):
@@ -92,7 +92,7 @@ def __init__(self, config: BaseConfig) -> None:
Args:
config: The test suite configuration.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self.config = config
self._ctx = get_ctx()
diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py
index 4f42bf6aeb..40dd7f0666 100644
--- a/dts/framework/testbed_model/node.py
+++ b/dts/framework/testbed_model/node.py
@@ -242,7 +242,7 @@ def get_node(node_identifier: NodeIdentifier) -> Node | None:
if node_identifier == "local":
return None
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
if node_identifier == "sut":
diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py
index 805a762c19..5b6ff2add5 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/framework/testbed_model/topology.py
@@ -96,7 +96,7 @@ def node_and_ports_from_id(self, node_identifier: NodeIdentifier) -> tuple[Node,
Raises:
InternalError: If the given `node_identifier` is invalid.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
match node_identifier:
@@ -180,7 +180,7 @@ def instantiate_crypto_ports(self) -> None:
Raises:
InternalError: If crypto virtual functions could not be created on a port.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
for port in ctx.sut_node.cryptodevs:
@@ -206,7 +206,7 @@ def instantiate_vf_ports(self) -> None:
Raises:
InternalError: If virtual function creation fails.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -235,7 +235,7 @@ def instantiate_vf_ports(self) -> None:
def delete_vf_ports(self) -> None:
"""Delete virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -246,7 +246,7 @@ def delete_vf_ports(self) -> None:
def delete_crypto_vf_ports(self) -> None:
"""Delete crypto virtual functions from the SUT node during test run teardown."""
- from framework.context import get_ctx
+ from api.context import get_ctx
ctx = get_ctx()
@@ -259,7 +259,7 @@ def bind_cryptodevs(self, driver: DriverKind):
Args:
driver: The driver to bind the crypto functions
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
self._bind_ports_to_drivers(get_ctx().sut_node, self.crypto_vf_ports, driver)
@@ -318,7 +318,7 @@ def _prepare_devbind_script(self) -> None:
Raises:
InternalError: If dpdk-devbind.py could not be found.
"""
- from framework.context import get_ctx
+ from api.context import get_ctx
local_script_path = Path("..", "usertools", "dpdk-devbind.py").resolve()
valid_script_path = local_script_path.exists()
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index f36b48a153..39784cbcac 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -31,7 +31,7 @@
)
from api.exception import SkippedTestException
from api.test import verify
-from framework.context import get_ctx
+from api.context import get_ctx
from framework.test_suite import BaseConfig, TestSuite, crypto_test
from framework.testbed_model.virtual_device import VirtualDevice
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 1e99b82b8c..c018efb1f7 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -21,7 +21,7 @@
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.context import filter_cores
+from api.context import filter_cores
from framework.test_suite import TestSuite, func_test
from framework.testbed_model.cpu import LogicalCoreCount
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 4/7] dts: move testbed model from framework to API
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (2 preceding siblings ...)
2026-04-30 22:06 ` [PATCH v4 3/7] dts: move context " Dean Marx
@ 2026-04-30 22:06 ` Dean Marx
2026-04-30 22:06 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
` (2 subsequent siblings)
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 22:06 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Multiple test suites import modules from testbed model
in the framework. Move this directory to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
...y.rst => api.testbed_model.capability.rst} | 2 +-
...odel.cpu.rst => api.testbed_model.cpu.rst} | 2 +-
...st => api.testbed_model.linux_session.rst} | 2 +-
...el.node.rst => api.testbed_model.node.rst} | 2 +-
...n.rst => api.testbed_model.os_session.rst} | 2 +-
...el.port.rst => api.testbed_model.port.rst} | 2 +-
...st => api.testbed_model.posix_session.rst} | 2 +-
doc/api/dts/api.testbed_model.rst | 28 +++++++++++++++++++
...ogy.rst => api.testbed_model.topology.rst} | 2 +-
...generator.capturing_traffic_generator.rst} | 2 +-
.../api.testbed_model.traffic_generator.rst | 16 +++++++++++
...testbed_model.traffic_generator.scapy.rst} | 2 +-
...l.traffic_generator.traffic_generator.rst} | 2 +-
...t => api.testbed_model.virtual_device.rst} | 2 +-
doc/api/dts/framework.testbed_model.rst | 28 -------------------
...mework.testbed_model.traffic_generator.rst | 16 -----------
doc/api/dts/index.rst | 2 +-
dts/api/artifact.py | 2 +-
dts/api/capabilities.py | 8 +++---
dts/api/context.py | 10 +++----
dts/api/cryptodev/__init__.py | 2 +-
dts/api/packet.py | 12 ++++----
dts/api/test.py | 2 +-
.../testbed_model/__init__.py | 0
.../testbed_model/capability.py | 10 +++----
dts/{framework => api}/testbed_model/cpu.py | 2 +-
.../testbed_model/linux_session.py | 2 +-
dts/{framework => api}/testbed_model/node.py | 0
.../testbed_model/os_session.py | 6 ++--
dts/{framework => api}/testbed_model/port.py | 0
.../testbed_model/posix_session.py | 0
.../testbed_model/topology.py | 4 +--
.../traffic_generator/__init__.py | 2 +-
.../capturing_traffic_generator.py | 2 +-
.../performance_traffic_generator.py | 0
.../testbed_model/traffic_generator/scapy.py | 12 ++++----
.../traffic_generator/traffic_generator.py | 6 ++--
.../testbed_model/traffic_generator/trex.py | 14 +++++-----
.../testbed_model/virtual_device.py | 0
dts/api/testpmd/__init__.py | 2 +-
dts/framework/params/eal.py | 6 ++--
dts/framework/params/types.py | 6 ++--
dts/framework/remote_session/blocking_app.py | 2 +-
dts/framework/remote_session/dpdk.py | 10 +++----
dts/framework/remote_session/dpdk_shell.py | 2 +-
.../remote_session/interactive_shell.py | 4 +--
dts/framework/runner.py | 2 +-
dts/framework/test_result.py | 2 +-
dts/framework/test_run.py | 18 ++++++------
dts/framework/test_suite.py | 4 +--
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 4 +--
dts/tests/TestSuite_l2fwd.py | 4 +--
dts/tests/TestSuite_packet_capture.py | 8 +++---
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 6 ++--
57 files changed, 148 insertions(+), 148 deletions(-)
rename doc/api/dts/{framework.testbed_model.capability.rst => api.testbed_model.capability.rst} (74%)
rename doc/api/dts/{framework.testbed_model.cpu.rst => api.testbed_model.cpu.rst} (78%)
rename doc/api/dts/{framework.testbed_model.linux_session.rst => api.testbed_model.linux_session.rst} (74%)
rename doc/api/dts/{framework.testbed_model.node.rst => api.testbed_model.node.rst} (71%)
rename doc/api/dts/{framework.testbed_model.os_session.rst => api.testbed_model.os_session.rst} (76%)
rename doc/api/dts/{framework.testbed_model.port.rst => api.testbed_model.port.rst} (77%)
rename doc/api/dts/{framework.testbed_model.posix_session.rst => api.testbed_model.posix_session.rst} (74%)
create mode 100644 doc/api/dts/api.testbed_model.rst
rename doc/api/dts/{framework.testbed_model.topology.rst => api.testbed_model.topology.rst} (73%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.capturing_traffic_generator.rst => api.testbed_model.traffic_generator.capturing_traffic_generator.rst} (68%)
create mode 100644 doc/api/dts/api.testbed_model.traffic_generator.rst
rename doc/api/dts/{framework.testbed_model.traffic_generator.scapy.rst => api.testbed_model.traffic_generator.scapy.rst} (70%)
rename doc/api/dts/{framework.testbed_model.traffic_generator.traffic_generator.rst => api.testbed_model.traffic_generator.traffic_generator.rst} (65%)
rename doc/api/dts/{framework.testbed_model.virtual_device.rst => api.testbed_model.virtual_device.rst} (72%)
delete mode 100644 doc/api/dts/framework.testbed_model.rst
delete mode 100644 doc/api/dts/framework.testbed_model.traffic_generator.rst
rename dts/{framework => api}/testbed_model/__init__.py (100%)
rename dts/{framework => api}/testbed_model/capability.py (99%)
rename dts/{framework => api}/testbed_model/cpu.py (99%)
rename dts/{framework => api}/testbed_model/linux_session.py (99%)
rename dts/{framework => api}/testbed_model/node.py (100%)
rename dts/{framework => api}/testbed_model/os_session.py (99%)
rename dts/{framework => api}/testbed_model/port.py (100%)
rename dts/{framework => api}/testbed_model/posix_session.py (100%)
rename dts/{framework => api}/testbed_model/topology.py (99%)
rename dts/{framework => api}/testbed_model/traffic_generator/__init__.py (97%)
rename dts/{framework => api}/testbed_model/traffic_generator/capturing_traffic_generator.py (99%)
rename dts/{framework => api}/testbed_model/traffic_generator/performance_traffic_generator.py (100%)
rename dts/{framework => api}/testbed_model/traffic_generator/scapy.py (98%)
rename dts/{framework => api}/testbed_model/traffic_generator/traffic_generator.py (94%)
rename dts/{framework => api}/testbed_model/traffic_generator/trex.py (97%)
rename dts/{framework => api}/testbed_model/virtual_device.py (100%)
diff --git a/doc/api/dts/framework.testbed_model.capability.rst b/doc/api/dts/api.testbed_model.capability.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.capability.rst
rename to doc/api/dts/api.testbed_model.capability.rst
index fab91cad83..88e396dddb 100644
--- a/doc/api/dts/framework.testbed_model.capability.rst
+++ b/doc/api/dts/api.testbed_model.capability.rst
@@ -3,6 +3,6 @@
capability - Testbed Capabilities
=================================
-.. automodule:: framework.testbed_model.capability
+.. automodule:: api.testbed_model.capability
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.cpu.rst b/doc/api/dts/api.testbed_model.cpu.rst
similarity index 78%
rename from doc/api/dts/framework.testbed_model.cpu.rst
rename to doc/api/dts/api.testbed_model.cpu.rst
index 997f2a9795..dbbb29480a 100644
--- a/doc/api/dts/framework.testbed_model.cpu.rst
+++ b/doc/api/dts/api.testbed_model.cpu.rst
@@ -3,6 +3,6 @@
cpu - CPU Representation and Utilities
======================================
-.. automodule:: framework.testbed_model.cpu
+.. automodule:: api.testbed_model.cpu
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.linux_session.rst b/doc/api/dts/api.testbed_model.linux_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.linux_session.rst
rename to doc/api/dts/api.testbed_model.linux_session.rst
index 7567816199..cfe79d8bca 100644
--- a/doc/api/dts/framework.testbed_model.linux_session.rst
+++ b/doc/api/dts/api.testbed_model.linux_session.rst
@@ -3,6 +3,6 @@
linux\_session - Linux Remote Session
=====================================
-.. automodule:: framework.testbed_model.linux_session
+.. automodule:: api.testbed_model.linux_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.node.rst b/doc/api/dts/api.testbed_model.node.rst
similarity index 71%
rename from doc/api/dts/framework.testbed_model.node.rst
rename to doc/api/dts/api.testbed_model.node.rst
index 23c6c46a00..15f522e5f7 100644
--- a/doc/api/dts/framework.testbed_model.node.rst
+++ b/doc/api/dts/api.testbed_model.node.rst
@@ -3,6 +3,6 @@
node - Base Node
================
-.. automodule:: framework.testbed_model.node
+.. automodule:: api.testbed_model.node
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.os_session.rst b/doc/api/dts/api.testbed_model.os_session.rst
similarity index 76%
rename from doc/api/dts/framework.testbed_model.os_session.rst
rename to doc/api/dts/api.testbed_model.os_session.rst
index ecfb352311..e7e3f9894f 100644
--- a/doc/api/dts/framework.testbed_model.os_session.rst
+++ b/doc/api/dts/api.testbed_model.os_session.rst
@@ -3,6 +3,6 @@
os\_session - OS-aware Remote Session ABC
=========================================
-.. automodule:: framework.testbed_model.os_session
+.. automodule:: api.testbed_model.os_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.port.rst b/doc/api/dts/api.testbed_model.port.rst
similarity index 77%
rename from doc/api/dts/framework.testbed_model.port.rst
rename to doc/api/dts/api.testbed_model.port.rst
index fdb7ca8a1d..d64501aef0 100644
--- a/doc/api/dts/framework.testbed_model.port.rst
+++ b/doc/api/dts/api.testbed_model.port.rst
@@ -3,7 +3,7 @@
port - NIC Port Representation
==============================
-.. automodule:: framework.testbed_model.port
+.. automodule:: api.testbed_model.port
:members:
:show-inheritance:
:noindex:
diff --git a/doc/api/dts/framework.testbed_model.posix_session.rst b/doc/api/dts/api.testbed_model.posix_session.rst
similarity index 74%
rename from doc/api/dts/framework.testbed_model.posix_session.rst
rename to doc/api/dts/api.testbed_model.posix_session.rst
index e65585fd85..9f0e9ff18d 100644
--- a/doc/api/dts/framework.testbed_model.posix_session.rst
+++ b/doc/api/dts/api.testbed_model.posix_session.rst
@@ -3,6 +3,6 @@
posix\_session - Posix Remote Session
=====================================
-.. automodule:: framework.testbed_model.posix_session
+.. automodule:: api.testbed_model.posix_session
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.rst b/doc/api/dts/api.testbed_model.rst
new file mode 100644
index 0000000000..5e2e5189b2
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.rst
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+testbed\_model - Testbed Modelling Package
+==========================================
+
+.. automodule:: api.testbed_model
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 2
+
+ api.testbed_model.traffic_generator
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.os_session
+ api.testbed_model.linux_session
+ api.testbed_model.posix_session
+ api.testbed_model.node
+ api.testbed_model.capability
+ api.testbed_model.cpu
+ api.testbed_model.port
+ api.testbed_model.topology
+ api.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.topology.rst b/doc/api/dts/api.testbed_model.topology.rst
similarity index 73%
rename from doc/api/dts/framework.testbed_model.topology.rst
rename to doc/api/dts/api.testbed_model.topology.rst
index 496f2a895f..bb63fe38dc 100644
--- a/doc/api/dts/framework.testbed_model.topology.rst
+++ b/doc/api/dts/api.testbed_model.topology.rst
@@ -3,6 +3,6 @@
topology - Testbed Topology
===========================
-.. automodule:: framework.testbed_model.topology
+.. automodule:: api.testbed_model.topology
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
similarity index 68%
rename from doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
index 29fa834042..cfe03201a7 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.capturing_traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.capturing_traffic_generator.rst
@@ -3,6 +3,6 @@
capturing\_traffic\_generator - Base Capturing TG ABC
=====================================================
-.. automodule:: framework.testbed_model.traffic_generator.capturing_traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.capturing_traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/api.testbed_model.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.rst
new file mode 100644
index 0000000000..311bdcf6b9
--- /dev/null
+++ b/doc/api/dts/api.testbed_model.traffic_generator.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+traffic\_generator Subpackage
+=============================
+
+.. automodule:: api.testbed_model.traffic_generator
+ :members:
+ :show-inheritance:
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ api.testbed_model.traffic_generator.traffic_generator
+ api.testbed_model.traffic_generator.capturing_traffic_generator
+ api.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
similarity index 70%
rename from doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
index df78ac9514..949bb66632 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.scapy.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.scapy.rst
@@ -3,6 +3,6 @@
scapy - Capturing Traffic Generator
===================================
-.. automodule:: framework.testbed_model.traffic_generator.scapy
+.. automodule:: api.testbed_model.traffic_generator.scapy
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
similarity index 65%
rename from doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
rename to doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
index bfec728dee..1045e534b5 100644
--- a/doc/api/dts/framework.testbed_model.traffic_generator.traffic_generator.rst
+++ b/doc/api/dts/api.testbed_model.traffic_generator.traffic_generator.rst
@@ -3,6 +3,6 @@
traffic\_generator - Base TG ABC
================================
-.. automodule:: framework.testbed_model.traffic_generator.traffic_generator
+.. automodule:: api.testbed_model.traffic_generator.traffic_generator
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.virtual_device.rst b/doc/api/dts/api.testbed_model.virtual_device.rst
similarity index 72%
rename from doc/api/dts/framework.testbed_model.virtual_device.rst
rename to doc/api/dts/api.testbed_model.virtual_device.rst
index a6b0420e75..97adc895f6 100644
--- a/doc/api/dts/framework.testbed_model.virtual_device.rst
+++ b/doc/api/dts/api.testbed_model.virtual_device.rst
@@ -3,6 +3,6 @@
virtual\_device - Virtual Devices
=================================
-.. automodule:: framework.testbed_model.virtual_device
+.. automodule:: api.testbed_model.virtual_device
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.testbed_model.rst b/doc/api/dts/framework.testbed_model.rst
deleted file mode 100644
index f283178f6a..0000000000
--- a/doc/api/dts/framework.testbed_model.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-testbed\_model - Testbed Modelling Package
-==========================================
-
-.. automodule:: framework.testbed_model
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 2
-
- framework.testbed_model.traffic_generator
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.os_session
- framework.testbed_model.linux_session
- framework.testbed_model.posix_session
- framework.testbed_model.node
- framework.testbed_model.capability
- framework.testbed_model.cpu
- framework.testbed_model.port
- framework.testbed_model.topology
- framework.testbed_model.virtual_device
diff --git a/doc/api/dts/framework.testbed_model.traffic_generator.rst b/doc/api/dts/framework.testbed_model.traffic_generator.rst
deleted file mode 100644
index 24c250ee3a..0000000000
--- a/doc/api/dts/framework.testbed_model.traffic_generator.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. SPDX-License-Identifier: BSD-3-Clause
-
-traffic\_generator Subpackage
-=============================
-
-.. automodule:: framework.testbed_model.traffic_generator
- :members:
- :show-inheritance:
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- framework.testbed_model.traffic_generator.traffic_generator
- framework.testbed_model.traffic_generator.capturing_traffic_generator
- framework.testbed_model.traffic_generator.scapy
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 98269d6e80..f47e4af3f2 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -16,7 +16,7 @@ Packages
tests
api
- framework.testbed_model
+ api.testbed_model
framework.remote_session
framework.params
framework.config
diff --git a/dts/api/artifact.py b/dts/api/artifact.py
index f3dd07de56..74a8ac667f 100644
--- a/dts/api/artifact.py
+++ b/dts/api/artifact.py
@@ -48,9 +48,9 @@
from typing_extensions import Buffer
from api.exception import InternalError
+from api.testbed_model.node import Node, NodeIdentifier, get_node
from framework.logger import DTSLogger, get_dts_logger
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node, NodeIdentifier, get_node
TextMode: TypeAlias = (
Literal["r", "r+", "w", "w+", "a", "a+", "x", "x+"]
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 09bc538523..04fc20738b 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -23,7 +23,7 @@
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires_link_topology
+ from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -34,7 +34,7 @@ def hello_world_single_core(self):
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires_nic_capability
+ from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -235,7 +235,7 @@ def requires_link_topology(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import TopologyCapability
+ from api.testbed_model.capability import TopologyCapability
def add_required_topology(
test_case_or_suite: type["TestProtocol"],
@@ -258,7 +258,7 @@ def requires_nic_capability(
Returns:
The decorated test case or test suite.
"""
- from framework.testbed_model.capability import DecoratedNicCapability
+ from api.testbed_model.capability import DecoratedNicCapability
def add_required_capability(
test_case_or_suite: type["TestProtocol"],
diff --git a/dts/api/context.py b/dts/api/context.py
index 7e61c85998..13a2ad6c39 100644
--- a/dts/api/context.py
+++ b/dts/api/context.py
@@ -9,17 +9,17 @@
from typing import TYPE_CHECKING, Any, Optional, ParamSpec, Union
from api.exception import InternalError
+from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
+from api.testbed_model.node import Node
+from api.testbed_model.topology import Topology
from framework.remote_session.shell_pool import ShellPool
from framework.settings import SETTINGS
-from framework.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
+ from api.testbed_model.capability import TestProtocol
+ from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.test_suite import TestCase, TestSuite
- from framework.testbed_model.capability import TestProtocol
- from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
P = ParamSpec("P")
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 67dcb02130..1ba8e0d977 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -14,6 +14,7 @@
from typing_extensions import Unpack
+from api.context import get_ctx
from api.cryptodev.config import CryptoPmdParams, TestType
from api.cryptodev.types import (
CryptodevResults,
@@ -23,7 +24,6 @@
VerifyResults,
)
from api.exception import RemoteCommandExecutionError, SkippedTestException
-from api.context import get_ctx
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
diff --git a/dts/api/packet.py b/dts/api/packet.py
index 59f26da833..bf90961c26 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -27,16 +27,16 @@
from scapy.layers.l2 import Ether
from scapy.packet import Packet, Padding, raw
+from api.context import get_ctx
from api.exception import InternalError
from api.test import fail, log_debug
-from api.utils import get_packet_summaries
-from api.context import get_ctx
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
)
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficStats,
)
+from api.utils import get_packet_summaries
def send_packet_and_capture(
@@ -83,7 +83,7 @@ def send_packets_and_capture(
A list of received packets.
"""
from api.context import get_ctx
- from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
+ from api.testbed_model.traffic_generator.capturing_traffic_generator import (
CapturingTrafficGenerator,
)
@@ -340,7 +340,7 @@ def assess_performance_by_packet(
Returns:
Performance statistics of the generated test.
"""
- from framework.testbed_model.traffic_generator.performance_traffic_generator import (
+ from api.testbed_model.traffic_generator.performance_traffic_generator import (
PerformanceTrafficGenerator,
)
diff --git a/dts/api/test.py b/dts/api/test.py
index a1f2326075..03846639ad 100644
--- a/dts/api/test.py
+++ b/dts/api/test.py
@@ -10,8 +10,8 @@
from datetime import datetime
from api.artifact import Artifact
-from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from api.context import get_ctx
+from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
from framework.logger import DTSLogger
diff --git a/dts/framework/testbed_model/__init__.py b/dts/api/testbed_model/__init__.py
similarity index 100%
rename from dts/framework/testbed_model/__init__.py
rename to dts/api/testbed_model/__init__.py
diff --git a/dts/framework/testbed_model/capability.py b/dts/api/testbed_model/capability.py
similarity index 99%
rename from dts/framework/testbed_model/capability.py
rename to dts/api/testbed_model/capability.py
index 001b65994c..4e4e976be5 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -26,7 +26,7 @@
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import LinkTopology, requires
+ from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@func_test
@@ -37,7 +37,7 @@ def hello_world_single_core(self):
.. code:: python
from framework.test_suite import TestSuite, func_test
- from framework.testbed_model.capability import NicCapability, requires
+ from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
# other test cases may not require it
@@ -65,10 +65,10 @@ def test_scatter_mbuf_2048(self):
from api.capabilities import LinkTopology, NicCapability
from api.exception import ConfigurationError, InternalError, SkippedTestException
+from api.testbed_model.node import Node
+from api.testbed_model.port import DriverKind
+from api.testbed_model.topology import Topology
from framework.logger import get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import DriverKind
-from framework.testbed_model.topology import Topology
if TYPE_CHECKING:
from api.testpmd import TestPmd
diff --git a/dts/framework/testbed_model/cpu.py b/dts/api/testbed_model/cpu.py
similarity index 99%
rename from dts/framework/testbed_model/cpu.py
rename to dts/api/testbed_model/cpu.py
index 52ef196f84..ee754f5844 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/api/testbed_model/cpu.py
@@ -29,7 +29,7 @@
@unique
class Architecture(StrEnum):
- r"""The supported architectures of :class:`~framework.testbed_model.node.Node`\s."""
+ r"""The supported architectures of :class:`~api.testbed_model.node.Node`\s."""
#:
i686 = auto()
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
similarity index 99%
rename from dts/framework/testbed_model/linux_session.py
rename to dts/api/testbed_model/linux_session.py
index 69b0923744..7307b2abe2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -23,8 +23,8 @@
InternalError,
RemoteCommandExecutionError,
)
+from api.testbed_model.port import PortInfo
from api.utils import expand_range
-from framework.testbed_model.port import PortInfo
from .cpu import LogicalCore
from .port import Port
diff --git a/dts/framework/testbed_model/node.py b/dts/api/testbed_model/node.py
similarity index 100%
rename from dts/framework/testbed_model/node.py
rename to dts/api/testbed_model/node.py
diff --git a/dts/framework/testbed_model/os_session.py b/dts/api/testbed_model/os_session.py
similarity index 99%
rename from dts/framework/testbed_model/os_session.py
rename to dts/api/testbed_model/os_session.py
index 7bb339fab2..b1e0538ac9 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/api/testbed_model/os_session.py
@@ -73,11 +73,11 @@ class OSSessionInfo:
Attributes:
os_name: The name of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
os_version: The version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
kernel_version: The kernel version of the running operating system of
- the :class:`~framework.testbed_model.node.Node`.
+ the :class:`~api.testbed_model.node.Node`.
"""
os_name: str
diff --git a/dts/framework/testbed_model/port.py b/dts/api/testbed_model/port.py
similarity index 100%
rename from dts/framework/testbed_model/port.py
rename to dts/api/testbed_model/port.py
diff --git a/dts/framework/testbed_model/posix_session.py b/dts/api/testbed_model/posix_session.py
similarity index 100%
rename from dts/framework/testbed_model/posix_session.py
rename to dts/api/testbed_model/posix_session.py
diff --git a/dts/framework/testbed_model/topology.py b/dts/api/testbed_model/topology.py
similarity index 99%
rename from dts/framework/testbed_model/topology.py
rename to dts/api/testbed_model/topology.py
index 5b6ff2add5..11593d64d5 100644
--- a/dts/framework/testbed_model/topology.py
+++ b/dts/api/testbed_model/topology.py
@@ -19,8 +19,8 @@
from api.capabilities import LinkTopology
from api.exception import ConfigurationError, InternalError
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.node import Node, NodeIdentifier
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.node import Node, NodeIdentifier
from .port import DriverKind, Port, PortConfig
diff --git a/dts/framework/testbed_model/traffic_generator/__init__.py b/dts/api/testbed_model/traffic_generator/__init__.py
similarity index 97%
rename from dts/framework/testbed_model/traffic_generator/__init__.py
rename to dts/api/testbed_model/traffic_generator/__init__.py
index 324b5e88f3..11fa25448a 100644
--- a/dts/framework/testbed_model/traffic_generator/__init__.py
+++ b/dts/api/testbed_model/traffic_generator/__init__.py
@@ -15,12 +15,12 @@
"""
from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.test_run import (
ScapyTrafficGeneratorConfig,
TrafficGeneratorConfig,
TrexTrafficGeneratorConfig,
)
-from framework.testbed_model.node import Node
from .scapy import ScapyTrafficGenerator
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
similarity index 99%
rename from dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
index 2804d64990..db274e5e82 100644
--- a/dts/framework/testbed_model/traffic_generator/capturing_traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/capturing_traffic_generator.py
@@ -17,8 +17,8 @@
from scapy.packet import Packet
from api.artifact import Artifact
+from api.testbed_model.port import Port
from api.utils import get_packet_summaries
-from framework.testbed_model.port import Port
from .traffic_generator import TrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py b/dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
similarity index 100%
rename from dts/framework/testbed_model/traffic_generator/performance_traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/performance_traffic_generator.py
diff --git a/dts/framework/testbed_model/traffic_generator/scapy.py b/dts/api/testbed_model/traffic_generator/scapy.py
similarity index 98%
rename from dts/framework/testbed_model/traffic_generator/scapy.py
rename to dts/api/testbed_model/traffic_generator/scapy.py
index e983443548..215c57f93d 100644
--- a/dts/framework/testbed_model/traffic_generator/scapy.py
+++ b/dts/api/testbed_model/traffic_generator/scapy.py
@@ -26,15 +26,15 @@
from scapy.packet import Packet
from api.exception import InteractiveSSHSessionDeadError, InternalError
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from framework.config.node import OS
from framework.config.test_run import ScapyTrafficGeneratorConfig
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
from .capturing_traffic_generator import CapturingTrafficGenerator
diff --git a/dts/framework/testbed_model/traffic_generator/traffic_generator.py b/dts/api/testbed_model/traffic_generator/traffic_generator.py
similarity index 94%
rename from dts/framework/testbed_model/traffic_generator/traffic_generator.py
rename to dts/api/testbed_model/traffic_generator/traffic_generator.py
index fedce77fdf..5fd68e5144 100644
--- a/dts/framework/testbed_model/traffic_generator/traffic_generator.py
+++ b/dts/api/testbed_model/traffic_generator/traffic_generator.py
@@ -13,11 +13,11 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node
+from api.testbed_model.port import Port
+from api.testbed_model.topology import Topology
from framework.config.test_run import TrafficGeneratorConfig
from framework.logger import DTSLogger, get_dts_logger
-from framework.testbed_model.node import Node
-from framework.testbed_model.port import Port
-from framework.testbed_model.topology import Topology
class TrafficGenerator(ABC):
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/api/testbed_model/traffic_generator/trex.py
similarity index 97%
rename from dts/framework/testbed_model/traffic_generator/trex.py
rename to dts/api/testbed_model/traffic_generator/trex.py
index 2064703fcc..d97ed934c9 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/api/testbed_model/traffic_generator/trex.py
@@ -11,19 +11,19 @@
from scapy.packet import Packet
+from api.testbed_model.node import Node, create_session
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.topology import Topology
+from api.testbed_model.traffic_generator.performance_traffic_generator import (
+ PerformanceTrafficGenerator,
+ PerformanceTrafficStats,
+)
from api.utils import StrEnum
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
from framework.parser import TextParser
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.python_shell import PythonShell
-from framework.testbed_model.node import Node, create_session
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.topology import Topology
-from framework.testbed_model.traffic_generator.performance_traffic_generator import (
- PerformanceTrafficGenerator,
- PerformanceTrafficStats,
-)
@dataclass(slots=True)
diff --git a/dts/framework/testbed_model/virtual_device.py b/dts/api/testbed_model/virtual_device.py
similarity index 100%
rename from dts/framework/testbed_model/virtual_device.py
rename to dts/api/testbed_model/virtual_device.py
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index a528663c21..9f47a15433 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -32,6 +32,7 @@
from typing_extensions import Unpack
from api.capabilities import LinkTopology, NicCapability
+from api.context import get_ctx
from api.exception import InteractiveCommandExecutionError, InternalError
from api.testpmd.config import PortTopology, SimpleForwardingModes, TestPmdParams
from api.testpmd.types import (
@@ -55,7 +56,6 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from api.context import get_ctx
from framework.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
diff --git a/dts/framework/params/eal.py b/dts/framework/params/eal.py
index e84a20f02f..86bfd3fcc6 100644
--- a/dts/framework/params/eal.py
+++ b/dts/framework/params/eal.py
@@ -6,12 +6,12 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.virtual_device import VirtualDevice
from framework.params import Params, Switch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.virtual_device import VirtualDevice
if TYPE_CHECKING:
- from framework.testbed_model.port import Port
+ from api.testbed_model.port import Port
def _port_to_pci(port: "Port") -> str:
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 3c7650474c..f2fa69f8b8 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -32,6 +32,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TestType,
TLSVersion,
)
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.port import Port
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd.config import (
AnonMempoolAllocationMode,
EthPeer,
@@ -54,9 +57,6 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TxUDPPortPair,
)
from framework.params import Switch, YesNoSwitch
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.port import Port
-from framework.testbed_model.virtual_device import VirtualDevice
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 07db6dfeb0..84db3974b1 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,11 +31,11 @@
from typing_extensions import Self
from api.context import get_ctx
+from api.testbed_model.node import Node
from framework.params import Params
from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
-from framework.testbed_model.node import Node
P = TypeVar("P", bound=Params)
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 9f7cd60dfe..713a564d25 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -13,7 +13,12 @@
from pathlib import Path, PurePath
from typing import ClassVar, Final
+from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
+from api.testbed_model.node import Node
+from api.testbed_model.os_session import OSSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.utils import MesonArgs, TarCompressionFormat
from framework.config.test_run import (
DPDKBuildConfiguration,
@@ -26,14 +31,9 @@
RemoteDPDKTarballLocation,
RemoteDPDKTreeLocation,
)
-from api.context import get_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
-from framework.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
-from framework.testbed_model.node import Node
-from framework.testbed_model.os_session import OSSession
-from framework.testbed_model.virtual_device import VirtualDevice
@dataclass(slots=True, frozen=True)
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index a8f169787c..b807f9bdae 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,12 +11,12 @@
from pathlib import PurePath
from api.context import get_ctx
+from api.testbed_model.cpu import LogicalCoreList
from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
)
-from framework.testbed_model.cpu import LogicalCoreList
def compute_eal_params(
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index d138727c85..ec539bad95 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -29,16 +29,16 @@
from paramiko import Channel, channel
from typing_extensions import Self
+from api.context import get_ctx
from api.exception import (
InteractiveCommandExecutionError,
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
-from api.context import get_ctx
+from api.testbed_model.node import Node
from framework.logger import DTSLogger, get_dts_logger
from framework.params import Params
from framework.settings import SETTINGS
-from framework.testbed_model.node import Node
P = ParamSpec("P")
T = TypeVar("T", bound="InteractiveShell")
diff --git a/dts/framework/runner.py b/dts/framework/runner.py
index a0d8039a04..29be7b80fe 100644
--- a/dts/framework/runner.py
+++ b/dts/framework/runner.py
@@ -13,9 +13,9 @@
import textwrap
from api.exception import ConfigurationError
+from api.testbed_model.node import Node
from framework.config.common import ValidationContext
from framework.test_run import TestRun
-from framework.testbed_model.node import Node
from .config import Configuration, load_config
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 3cecb928ca..5f945163ce 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -36,9 +36,9 @@
from typing_extensions import OrderedDict
from api.exception import DTSError, ErrorSeverity, InternalError
+from api.testbed_model.os_session import OSSessionInfo
from framework.remote_session.dpdk import DPDKBuildInfo
from framework.settings import SETTINGS
-from framework.testbed_model.os_session import OSSessionInfo
class Result(IntEnum):
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 605a916ebb..790fbf997d 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -106,22 +106,22 @@
from types import MethodType
from typing import ClassVar, Protocol, Union
+from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
+from api.testbed_model.capability import (
+ Capability,
+ get_supported_capabilities,
+ test_if_supported,
+)
+from api.testbed_model.node import Node
+from api.testbed_model.topology import PortLink, Topology
+from api.testbed_model.traffic_generator import create_traffic_generator
from framework.config.test_run import TestRunConfiguration
-from api.context import Context, init_ctx
from framework.logger import DTSLogger, get_dts_logger
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
-from framework.testbed_model.capability import (
- Capability,
- get_supported_capabilities,
- test_if_supported,
-)
-from framework.testbed_model.node import Node
-from framework.testbed_model.topology import PortLink, Topology
-from framework.testbed_model.traffic_generator import create_traffic_generator
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 6066f7a77a..786cfc7bff 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -30,10 +30,10 @@
from typing_extensions import Self
from api.exception import ConfigurationError, InternalError
+from api.testbed_model.capability import TestProtocol
+from api.testbed_model.topology import Topology
from api.utils import to_pascal_case
from framework.config.common import FrozenModel
-from framework.testbed_model.capability import TestProtocol
-from framework.testbed_model.topology import Topology
from .logger import DTSLogger, get_dts_logger
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index c57231de22..97e03b8fb7 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,9 +11,9 @@
requires_link_topology,
)
from api.test import verify
+from api.testbed_model.port import Port
from api.testpmd import TestPmd
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.port import Port
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index 39784cbcac..fc4b3cb308 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -13,6 +13,7 @@
LinkTopology,
requires_link_topology,
)
+from api.context import get_ctx
from api.cryptodev import Cryptodev
from api.cryptodev.config import (
AeadAlgName,
@@ -31,9 +32,8 @@
)
from api.exception import SkippedTestException
from api.test import verify
-from api.context import get_ctx
+from api.testbed_model.virtual_device import VirtualDevice
from framework.test_suite import BaseConfig, TestSuite, crypto_test
-from framework.testbed_model.virtual_device import VirtualDevice
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index c018efb1f7..5650366c36 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -13,17 +13,17 @@
requires_link_topology,
requires_nic_capability,
)
+from api.context import filter_cores
from api.packet import (
get_expected_packets,
match_all_packets,
send_packets_and_capture,
)
+from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from api.context import filter_cores
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.cpu import LogicalCoreCount
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 4bd15e2401..042b7019aa 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,15 +36,15 @@
send_packets_and_capture,
)
from api.test import verify
+from api.testbed_model.cpu import LogicalCoreList
+from api.testbed_model.traffic_generator.capturing_traffic_generator import (
+ PacketFilteringConfig,
+)
from api.testpmd import TestPmd
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.cpu import LogicalCoreList
-from framework.testbed_model.traffic_generator.capturing_traffic_generator import (
- PacketFilteringConfig,
-)
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index fce83604a6..656e2e4bb7 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,12 +19,12 @@
requires_link_topology,
)
from api.test import verify
+from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.linux_session import LinuxSession
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index c57a12c932..91a6d4eb9f 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,11 +18,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
from api.utils import generate_random_packets
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.virtual_device import VirtualDevice
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index bdecdb76fd..c649aac197 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,12 +12,12 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.testbed_model.capability import requires
+from api.testbed_model.linux_session import LinuxSession
+from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
from framework.test_suite import TestSuite, func_test
-from framework.testbed_model.capability import requires
-from framework.testbed_model.linux_session import LinuxSession
-from framework.testbed_model.virtual_device import VirtualDevice
class TestVirtioFwd(TestSuite):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 5/7] dts: move test suite module from framework to API
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (3 preceding siblings ...)
2026-04-30 22:06 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
@ 2026-04-30 22:06 ` Dean Marx
2026-04-30 22:06 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
2026-04-30 22:06 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 22:06 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Currently, each test suite imports the TestSuite class
from the DTS framework to use as a base class.
However, the goal for 26.07 is to move all test suite
imports to the API module. Moves and updates the test_suite
file to the API directory, and updates all files that import
test_suite to reflect this change.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.test_suite.rst => api.test_suite.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/capabilities.py | 6 +++---
dts/api/context.py | 2 +-
dts/api/packet.py | 2 +-
dts/{framework => api}/test_suite.py | 3 +--
dts/api/testbed_model/capability.py | 6 +++---
dts/framework/config/__init__.py | 4 ++--
dts/framework/config/test_run.py | 8 ++++----
dts/framework/test_run.py | 2 +-
dts/tests/TestSuite_blocklist.py | 2 +-
dts/tests/TestSuite_checksum_offload.py | 2 +-
dts/tests/TestSuite_cryptodev_throughput.py | 2 +-
dts/tests/TestSuite_dual_vlan.py | 2 +-
dts/tests/TestSuite_dynamic_config.py | 2 +-
dts/tests/TestSuite_dynamic_queue_conf.py | 2 +-
dts/tests/TestSuite_hello_world.py | 2 +-
dts/tests/TestSuite_l2fwd.py | 2 +-
dts/tests/TestSuite_mac_filter.py | 2 +-
dts/tests/TestSuite_mtu.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +-
| 2 +-
dts/tests/TestSuite_port_control.py | 2 +-
dts/tests/TestSuite_port_restart_config_persistency.py | 2 +-
dts/tests/TestSuite_port_stats.py | 2 +-
dts/tests/TestSuite_promisc_support.py | 2 +-
dts/tests/TestSuite_qinq.py | 2 +-
dts/tests/TestSuite_queue_start_stop.py | 2 +-
dts/tests/TestSuite_rte_flow.py | 2 +-
dts/tests/TestSuite_rx_tx_offload.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
dts/tests/TestSuite_smoke_tests.py | 2 +-
dts/tests/TestSuite_softnic.py | 2 +-
dts/tests/TestSuite_uni_pkt.py | 2 +-
dts/tests/TestSuite_virtio_fwd.py | 2 +-
dts/tests/TestSuite_vlan.py | 2 +-
37 files changed, 45 insertions(+), 46 deletions(-)
rename doc/api/dts/{framework.test_suite.rst => api.test_suite.rst} (81%)
rename dts/{framework => api}/test_suite.py (99%)
diff --git a/doc/api/dts/framework.test_suite.rst b/doc/api/dts/api.test_suite.rst
similarity index 81%
rename from doc/api/dts/framework.test_suite.rst
rename to doc/api/dts/api.test_suite.rst
index 9517f51a4a..4acb7b103a 100644
--- a/doc/api/dts/framework.test_suite.rst
+++ b/doc/api/dts/api.test_suite.rst
@@ -3,6 +3,6 @@
test\_suite - Common Test Suite Features
========================================
-.. automodule:: framework.test_suite
+.. automodule:: api.test_suite
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index f47e4af3f2..7c282bbba1 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -29,7 +29,7 @@ Modules
framework.runner
framework.test_run
- framework.test_suite
+ api.test_suite
framework.test_result
framework.settings
api.context
diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 04fc20738b..a4d6b2b424 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -22,7 +22,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import LinkTopology, requires_link_topology
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -33,7 +33,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import NicCapability, requires_nic_capability
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -47,7 +47,7 @@ def test_scatter_mbuf_2048(self):
from typing import TYPE_CHECKING, Callable
if TYPE_CHECKING:
- from framework.test_suite import TestProtocol
+ from api.test_suite import TestProtocol
class LinkTopology(IntEnum):
diff --git a/dts/api/context.py b/dts/api/context.py
index 13a2ad6c39..7ed4cc5665 100644
--- a/dts/api/context.py
+++ b/dts/api/context.py
@@ -16,10 +16,10 @@
from framework.settings import SETTINGS
if TYPE_CHECKING:
+ from api.test_suite import TestCase, TestSuite
from api.testbed_model.capability import TestProtocol
from api.testbed_model.traffic_generator.traffic_generator import TrafficGenerator
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
- from framework.test_suite import TestCase, TestSuite
P = ParamSpec("P")
diff --git a/dts/api/packet.py b/dts/api/packet.py
index bf90961c26..873b8f0324 100644
--- a/dts/api/packet.py
+++ b/dts/api/packet.py
@@ -175,7 +175,7 @@ def adjust_addresses(packets: list[Packet], expected: bool = False) -> list[Pack
Raises:
InternalError: If no tests are running.
"""
- from framework.test_suite import TestSuite
+ from api.test_suite import TestSuite
if get_ctx().local.current_test_suite is None:
raise InternalError("No current test suite, tests aren't running?")
diff --git a/dts/framework/test_suite.py b/dts/api/test_suite.py
similarity index 99%
rename from dts/framework/test_suite.py
rename to dts/api/test_suite.py
index 786cfc7bff..0822f9bfe5 100644
--- a/dts/framework/test_suite.py
+++ b/dts/api/test_suite.py
@@ -34,8 +34,7 @@
from api.testbed_model.topology import Topology
from api.utils import to_pascal_case
from framework.config.common import FrozenModel
-
-from .logger import DTSLogger, get_dts_logger
+from framework.logger import DTSLogger, get_dts_logger
if TYPE_CHECKING:
from api.context import Context
diff --git a/dts/api/testbed_model/capability.py b/dts/api/testbed_model/capability.py
index 4e4e976be5..95583261d8 100644
--- a/dts/api/testbed_model/capability.py
+++ b/dts/api/testbed_model/capability.py
@@ -25,7 +25,7 @@
Examples:
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import LinkTopology, requires
# The whole test suite (each test case within) doesn't require any links.
@requires_link_topology(LinkTopology.NO_LINK)
@@ -36,7 +36,7 @@ def hello_world_single_core(self):
.. code:: python
- from framework.test_suite import TestSuite, func_test
+ from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import NicCapability, requires
class TestPmdBufferScatter(TestSuite):
# only the test case requires the SCATTERED_RX_ENABLED capability
@@ -71,8 +71,8 @@ def test_scatter_mbuf_2048(self):
from framework.logger import get_dts_logger
if TYPE_CHECKING:
+ from api.test_suite import TestCase
from api.testpmd import TestPmd
- from framework.test_suite import TestCase
P = ParamSpec("P")
TestPmdMethod = Callable[Concatenate["TestPmd", P], Any]
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index 566dc7c4a2..3a3580aaf7 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -43,7 +43,7 @@
# Import only if type checking or building docs, to prevent circular imports.
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig
+ from api.test_suite import BaseConfig
NodesConfig = Annotated[list[NodeConfiguration], Field(min_length=1)]
@@ -182,7 +182,7 @@ def load_config(ctx: ValidationContext) -> Configuration:
nodes = _load_and_parse_model(ctx["settings"].nodes_config_path, NodesConfig, ctx)
try:
- from framework.test_suite import BaseConfig as BaseConfig
+ from api.test_suite import BaseConfig as BaseConfig
Configuration.model_rebuild()
return Configuration.model_validate(
diff --git a/dts/framework/config/test_run.py b/dts/framework/config/test_run.py
index 977067f42a..81e3dba79b 100644
--- a/dts/framework/config/test_run.py
+++ b/dts/framework/config/test_run.py
@@ -33,7 +33,7 @@
from .common import FrozenModel, load_fields_from_settings
if TYPE_CHECKING:
- from framework.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
+ from api.test_suite import BaseConfig, TestCase, TestSuite, TestSuiteSpec
@unique
@@ -230,7 +230,7 @@ class TestSuiteConfig(FrozenModel):
@cached_property
def test_suite_spec(self) -> "TestSuiteSpec":
"""The specification of the requested test suite."""
- from framework.test_suite import find_by_name
+ from api.test_suite import find_by_name
test_suite_spec = find_by_name(self.test_suite_name)
assert (
@@ -280,7 +280,7 @@ def fetch_all_test_suites() -> list[TestSuiteConfig]:
This function does not include the smoke tests.
"""
- from framework.test_suite import AVAILABLE_TEST_SUITES
+ from api.test_suite import AVAILABLE_TEST_SUITES
return [
TestSuiteConfig(test_suite=test_suite.name)
@@ -506,7 +506,7 @@ def filter_tests(
self, tests_config: dict[str, "BaseConfig"]
) -> Iterable[tuple[type["TestSuite"], "BaseConfig", deque[type["TestCase"]]]]:
"""Filter test suites and cases selected for execution."""
- from framework.test_suite import TestCaseType
+ from api.test_suite import TestCaseType
test_suites = [TestSuiteConfig(test_suite="smoke_tests")]
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 790fbf997d..c133fbecb0 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -108,6 +108,7 @@
from api.context import Context, init_ctx
from api.exception import InternalError, SkippedTestException, TestCaseVerifyError
+from api.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
from api.testbed_model.capability import (
Capability,
get_supported_capabilities,
@@ -121,7 +122,6 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
TestScenario = tuple[type[TestSuite], BaseConfig, deque[type[TestCase]]]
diff --git a/dts/tests/TestSuite_blocklist.py b/dts/tests/TestSuite_blocklist.py
index 97e03b8fb7..31e69c0de9 100644
--- a/dts/tests/TestSuite_blocklist.py
+++ b/dts/tests/TestSuite_blocklist.py
@@ -11,9 +11,9 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.port import Port
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestBlocklist(TestSuite):
diff --git a/dts/tests/TestSuite_checksum_offload.py b/dts/tests/TestSuite_checksum_offload.py
index 90ca798e56..a2ea13991b 100644
--- a/dts/tests/TestSuite_checksum_offload.py
+++ b/dts/tests/TestSuite_checksum_offload.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import ChecksumOffloadOptions, PacketOffloadFlag
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_IPV4_CKSUM)
diff --git a/dts/tests/TestSuite_cryptodev_throughput.py b/dts/tests/TestSuite_cryptodev_throughput.py
index fc4b3cb308..d2a6cbab94 100644
--- a/dts/tests/TestSuite_cryptodev_throughput.py
+++ b/dts/tests/TestSuite_cryptodev_throughput.py
@@ -32,8 +32,8 @@
)
from api.exception import SkippedTestException
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, crypto_test
from api.testbed_model.virtual_device import VirtualDevice
-from framework.test_suite import BaseConfig, TestSuite, crypto_test
config_list: list[dict[str, int | float | str]] = [
{"buff_size": 64, "Gbps": 1.00},
diff --git a/dts/tests/TestSuite_dual_vlan.py b/dts/tests/TestSuite_dual_vlan.py
index 1b77dd2b47..f3347a6d52 100644
--- a/dts/tests/TestSuite_dual_vlan.py
+++ b/dts/tests/TestSuite_dual_vlan.py
@@ -21,9 +21,9 @@
from api.capabilities import LinkTopology, requires_link_topology
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestDualVlan(TestSuite):
diff --git a/dts/tests/TestSuite_dynamic_config.py b/dts/tests/TestSuite_dynamic_config.py
index 7204ec4f73..b9e2c30da1 100644
--- a/dts/tests/TestSuite_dynamic_config.py
+++ b/dts/tests/TestSuite_dynamic_config.py
@@ -27,9 +27,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py
index b62efa2b42..24584c7d60 100644
--- a/dts/tests/TestSuite_dynamic_queue_conf.py
+++ b/dts/tests/TestSuite_dynamic_queue_conf.py
@@ -38,9 +38,9 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
def setup_and_teardown_test(
diff --git a/dts/tests/TestSuite_hello_world.py b/dts/tests/TestSuite_hello_world.py
index bf1a93c782..cd62eb8f3e 100644
--- a/dts/tests/TestSuite_hello_world.py
+++ b/dts/tests/TestSuite_hello_world.py
@@ -9,8 +9,8 @@
"""
from api.test import log
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_l2fwd.py b/dts/tests/TestSuite_l2fwd.py
index 5650366c36..f237821a04 100644
--- a/dts/tests/TestSuite_l2fwd.py
+++ b/dts/tests/TestSuite_l2fwd.py
@@ -19,11 +19,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreCount
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer, SimpleForwardingModes
from api.utils import generate_random_packets
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mac_filter.py b/dts/tests/TestSuite_mac_filter.py
index b44822d31c..eb1413f336 100644
--- a/dts/tests/TestSuite_mac_filter.py
+++ b/dts/tests/TestSuite_mac_filter.py
@@ -26,8 +26,8 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packet_and_capture
from api.test import fail, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_mtu.py b/dts/tests/TestSuite_mtu.py
index 8355495d33..c264db299e 100644
--- a/dts/tests/TestSuite_mtu.py
+++ b/dts/tests/TestSuite_mtu.py
@@ -23,8 +23,8 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
STANDARD_FRAME = 1518 # --max-pkt-len will subtract l2 information at a minimum of 18 bytes.
JUMBO_FRAME = 9018
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index 042b7019aa..fd5cef5268 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -36,6 +36,7 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.traffic_generator.capturing_traffic_generator import (
PacketFilteringConfig,
@@ -44,7 +45,6 @@
from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
-from framework.test_suite import TestSuite, func_test
@dataclass(kw_only=True)
diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py
index 96da67ee7d..6c7f6d79fe 100644
--- a/dts/tests/TestSuite_pmd_buffer_scatter.py
+++ b/dts/tests/TestSuite_pmd_buffer_scatter.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
--git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
index 4df273e3e1..162e08ccbc 100644
--- a/dts/tests/TestSuite_pmd_rss.py
+++ b/dts/tests/TestSuite_pmd_rss.py
@@ -23,6 +23,7 @@
from api.exception import InteractiveCommandExecutionError
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import BaseConfig, TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import (
@@ -31,7 +32,6 @@
TestPmdVerbosePacket,
)
from api.utils import StrEnum
-from framework.test_suite import BaseConfig, TestSuite, func_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_port_control.py b/dts/tests/TestSuite_port_control.py
index 6be47838d0..5b960cb3a3 100644
--- a/dts/tests/TestSuite_port_control.py
+++ b/dts/tests/TestSuite_port_control.py
@@ -18,9 +18,9 @@
)
from api.packet import send_packets_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_port_restart_config_persistency.py b/dts/tests/TestSuite_port_restart_config_persistency.py
index 4ea22b6d70..88df35d33c 100644
--- a/dts/tests/TestSuite_port_restart_config_persistency.py
+++ b/dts/tests/TestSuite_port_restart_config_persistency.py
@@ -14,9 +14,9 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import TestPmdPortFlowCtrl
-from framework.test_suite import TestSuite, func_test
ALTERNATIVE_MTU: int = 800
STANDARD_MTU: int = 1500
diff --git a/dts/tests/TestSuite_port_stats.py b/dts/tests/TestSuite_port_stats.py
index 3dc045f847..0328c6718c 100644
--- a/dts/tests/TestSuite_port_stats.py
+++ b/dts/tests/TestSuite_port_stats.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_promisc_support.py b/dts/tests/TestSuite_promisc_support.py
index a0c65dc662..c59c8c6078 100644
--- a/dts/tests/TestSuite_promisc_support.py
+++ b/dts/tests/TestSuite_promisc_support.py
@@ -21,8 +21,8 @@
send_packets_and_capture,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_qinq.py b/dts/tests/TestSuite_qinq.py
index 505d71dbc8..5dde37d4db 100644
--- a/dts/tests/TestSuite_qinq.py
+++ b/dts/tests/TestSuite_qinq.py
@@ -18,8 +18,8 @@
from api.capabilities import NicCapability, requires_nic_capability
from api.packet import send_packet_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
-from framework.test_suite import TestSuite, func_test
class TestQinq(TestSuite):
diff --git a/dts/tests/TestSuite_queue_start_stop.py b/dts/tests/TestSuite_queue_start_stop.py
index e9048d4245..6935f395c1 100644
--- a/dts/tests/TestSuite_queue_start_stop.py
+++ b/dts/tests/TestSuite_queue_start_stop.py
@@ -24,9 +24,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.TWO_LINKS)
diff --git a/dts/tests/TestSuite_rte_flow.py b/dts/tests/TestSuite_rte_flow.py
index 7e50a075ac..8c5c59edec 100644
--- a/dts/tests/TestSuite_rte_flow.py
+++ b/dts/tests/TestSuite_rte_flow.py
@@ -28,9 +28,9 @@
)
from api.packet import send_packet_and_capture
from api.test import fail, log, verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import FlowRule
-from framework.test_suite import TestSuite, func_test
@dataclass
diff --git a/dts/tests/TestSuite_rx_tx_offload.py b/dts/tests/TestSuite_rx_tx_offload.py
index b0da627d3c..c8d24baaae 100644
--- a/dts/tests/TestSuite_rx_tx_offload.py
+++ b/dts/tests/TestSuite_rx_tx_offload.py
@@ -13,12 +13,12 @@
requires_nic_capability,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.types import (
OffloadConfiguration,
RxTxLiteralSwitch,
)
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.ONE_LINK)
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index 1e7ab7b036..f1eb435759 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -22,10 +22,10 @@
)
from api.packet import assess_performance_by_packet
from api.test import verify, write_performance_json
+from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
from framework.params.types import TestPmdParamsDict
-from framework.test_suite import BaseConfig, TestSuite, perf_test
class Config(BaseConfig):
diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py
index 656e2e4bb7..b3eb325fc0 100644
--- a/dts/tests/TestSuite_smoke_tests.py
+++ b/dts/tests/TestSuite_smoke_tests.py
@@ -19,12 +19,12 @@
requires_link_topology,
)
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.linux_session import LinuxSession
from api.testpmd import TestPmd
from api.utils import REGEX_FOR_PCI_ADDRESS
from framework.config.node import PortConfig
from framework.settings import SETTINGS
-from framework.test_suite import TestSuite, func_test
@requires_link_topology(LinkTopology.NO_LINK)
diff --git a/dts/tests/TestSuite_softnic.py b/dts/tests/TestSuite_softnic.py
index 91a6d4eb9f..05a6d3aa18 100644
--- a/dts/tests/TestSuite_softnic.py
+++ b/dts/tests/TestSuite_softnic.py
@@ -18,11 +18,11 @@
match_all_packets,
send_packets_and_capture,
)
+from api.test_suite import TestSuite, func_test
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import EthPeer
from api.utils import generate_random_packets
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PHYSICAL_FUNCTION)
diff --git a/dts/tests/TestSuite_uni_pkt.py b/dts/tests/TestSuite_uni_pkt.py
index 222276ce67..d83185d1b2 100644
--- a/dts/tests/TestSuite_uni_pkt.py
+++ b/dts/tests/TestSuite_uni_pkt.py
@@ -25,10 +25,10 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
from api.testpmd.types import RtePTypes, TestPmdVerbosePacket
-from framework.test_suite import TestSuite, func_test
class TestUniPkt(TestSuite):
diff --git a/dts/tests/TestSuite_virtio_fwd.py b/dts/tests/TestSuite_virtio_fwd.py
index c649aac197..2c10478df3 100644
--- a/dts/tests/TestSuite_virtio_fwd.py
+++ b/dts/tests/TestSuite_virtio_fwd.py
@@ -12,12 +12,12 @@
from api.capabilities import LinkTopology
from api.packet import send_packets_and_capture
from api.test import log, verify
+from api.test_suite import TestSuite, func_test
from api.testbed_model.capability import requires
from api.testbed_model.linux_session import LinuxSession
from api.testbed_model.virtual_device import VirtualDevice
from api.testpmd import TestPmd
from api.testpmd.config import PortTopology, SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
class TestVirtioFwd(TestSuite):
diff --git a/dts/tests/TestSuite_vlan.py b/dts/tests/TestSuite_vlan.py
index 898673fc86..975e87b128 100644
--- a/dts/tests/TestSuite_vlan.py
+++ b/dts/tests/TestSuite_vlan.py
@@ -23,9 +23,9 @@
)
from api.packet import send_packet_and_capture
from api.test import verify
+from api.test_suite import TestSuite, func_test
from api.testpmd import TestPmd
from api.testpmd.config import SimpleForwardingModes
-from framework.test_suite import TestSuite, func_test
@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_VLAN_FILTER)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 6/7] dts: move params directory from framework to API
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (4 preceding siblings ...)
2026-04-30 22:06 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
@ 2026-04-30 22:06 ` Dean Marx
2026-04-30 22:06 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 22:06 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
The params directory is imported in test suites such as
packet capture to use as a base class for dumpcap.
Move this to the API.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
.../dts/{framework.params.eal.rst => api.params.eal.rst} | 2 +-
doc/api/dts/{framework.params.rst => api.params.rst} | 6 +++---
.../{framework.params.types.rst => api.params.types.rst} | 2 +-
doc/api/dts/index.rst | 2 +-
dts/api/cryptodev/__init__.py | 2 +-
dts/api/cryptodev/config.py | 4 ++--
dts/{framework => api}/params/__init__.py | 0
dts/{framework => api}/params/eal.py | 2 +-
dts/{framework => api}/params/types.py | 8 ++++++--
dts/api/testpmd/__init__.py | 7 ++++++-
dts/api/testpmd/config.py | 6 +++---
dts/framework/remote_session/blocking_app.py | 4 ++--
dts/framework/remote_session/dpdk.py | 2 +-
dts/framework/remote_session/dpdk_shell.py | 2 +-
dts/framework/remote_session/interactive_shell.py | 2 +-
dts/tests/TestSuite_packet_capture.py | 2 +-
dts/tests/TestSuite_single_core_forward_perf.py | 2 +-
17 files changed, 32 insertions(+), 23 deletions(-)
rename doc/api/dts/{framework.params.eal.rst => api.params.eal.rst} (79%)
rename doc/api/dts/{framework.params.rst => api.params.rst} (71%)
rename doc/api/dts/{framework.params.types.rst => api.params.types.rst} (80%)
rename dts/{framework => api}/params/__init__.py (100%)
rename dts/{framework => api}/params/eal.py (97%)
rename dts/{framework => api}/params/types.py (97%)
diff --git a/doc/api/dts/framework.params.eal.rst b/doc/api/dts/api.params.eal.rst
similarity index 79%
rename from doc/api/dts/framework.params.eal.rst
rename to doc/api/dts/api.params.eal.rst
index 6999b00233..4531cb1fe1 100644
--- a/doc/api/dts/framework.params.eal.rst
+++ b/doc/api/dts/api.params.eal.rst
@@ -3,6 +3,6 @@
eal - EAL Parameters Modelling
==============================
-.. automodule:: framework.params.eal
+.. automodule:: api.params.eal
:members:
:show-inheritance:
diff --git a/doc/api/dts/framework.params.rst b/doc/api/dts/api.params.rst
similarity index 71%
rename from doc/api/dts/framework.params.rst
rename to doc/api/dts/api.params.rst
index d8c6af9667..3ea7f9215e 100644
--- a/doc/api/dts/framework.params.rst
+++ b/doc/api/dts/api.params.rst
@@ -3,7 +3,7 @@
params - Command Line Parameters Modelling
==========================================
-.. automodule:: framework.params
+.. automodule:: api.params
:members:
:show-inheritance:
@@ -11,5 +11,5 @@ params - Command Line Parameters Modelling
:hidden:
:maxdepth: 1
- framework.params.eal
- framework.params.types
+ api.params.eal
+ api.params.types
diff --git a/doc/api/dts/framework.params.types.rst b/doc/api/dts/api.params.types.rst
similarity index 80%
rename from doc/api/dts/framework.params.types.rst
rename to doc/api/dts/api.params.types.rst
index 6d609038be..4754b3a665 100644
--- a/doc/api/dts/framework.params.types.rst
+++ b/doc/api/dts/api.params.types.rst
@@ -3,6 +3,6 @@
params.types - Parameters Modelling Types
=========================================
-.. automodule:: framework.params.types
+.. automodule:: api.params.types
:members:
:show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index 7c282bbba1..e89e782ac0 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -18,7 +18,7 @@ Packages
api
api.testbed_model
framework.remote_session
- framework.params
+ api.params
framework.config
Modules
diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index 1ba8e0d977..bbfe3622c2 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -27,7 +27,7 @@
from framework.remote_session.dpdk_shell import compute_eal_params
if TYPE_CHECKING:
- from framework.params.types import CryptoPmdParamsDict
+ from api.params.types import CryptoPmdParamsDict
from pathlib import PurePath
diff --git a/dts/api/cryptodev/config.py b/dts/api/cryptodev/config.py
index a88e70d45c..3420c2fe91 100644
--- a/dts/api/cryptodev/config.py
+++ b/dts/api/cryptodev/config.py
@@ -6,9 +6,9 @@
from enum import auto
from typing import Literal
+from api.params import Params, Switch
+from api.params.eal import EalParams
from api.utils import StrEnum
-from framework.params import Params, Switch
-from framework.params.eal import EalParams
Silent = Literal[""]
diff --git a/dts/framework/params/__init__.py b/dts/api/params/__init__.py
similarity index 100%
rename from dts/framework/params/__init__.py
rename to dts/api/params/__init__.py
diff --git a/dts/framework/params/eal.py b/dts/api/params/eal.py
similarity index 97%
rename from dts/framework/params/eal.py
rename to dts/api/params/eal.py
index 86bfd3fcc6..64fa45ae12 100644
--- a/dts/framework/params/eal.py
+++ b/dts/api/params/eal.py
@@ -6,9 +6,9 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Literal
+from api.params import Params, Switch
from api.testbed_model.cpu import LogicalCoreList
from api.testbed_model.virtual_device import VirtualDevice
-from framework.params import Params, Switch
if TYPE_CHECKING:
from api.testbed_model.port import Port
diff --git a/dts/framework/params/types.py b/dts/api/params/types.py
similarity index 97%
rename from dts/framework/params/types.py
rename to dts/api/params/types.py
index f2fa69f8b8..2c215cfe54 100644
--- a/dts/framework/params/types.py
+++ b/dts/api/params/types.py
@@ -12,8 +12,10 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
params = TestPmdParams(**kwargs)
"""
+from __future__ import annotations
+
from pathlib import PurePath
-from typing import TypedDict
+from typing import TYPE_CHECKING, TypedDict
from api.cryptodev.config import (
AeadAlgName,
@@ -56,7 +58,9 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
TXRingParams,
TxUDPPortPair,
)
-from framework.params import Switch, YesNoSwitch
+
+if TYPE_CHECKING:
+ from api.params import Switch, YesNoSwitch
class EalParamsDict(TypedDict, total=False):
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index 9f47a15433..85ebe2ea65 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -14,6 +14,8 @@
testpmd.close()
"""
+from __future__ import annotations
+
import functools
import re
import time
@@ -21,6 +23,7 @@
from enum import Flag
from pathlib import PurePath
from typing import (
+ TYPE_CHECKING,
Any,
Callable,
ClassVar,
@@ -56,7 +59,9 @@
TxOffloadConfiguration,
VLANOffloadFlag,
)
-from framework.params.types import TestPmdParamsDict
+
+if TYPE_CHECKING:
+ from api.params.types import TestPmdParamsDict
from framework.remote_session.dpdk_shell import DPDKShell
from framework.remote_session.interactive_shell import only_active
from framework.settings import SETTINGS
diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index 8b688834ee..96fe5e79fb 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -13,8 +13,7 @@
from pathlib import PurePath
from typing import Literal, NamedTuple
-from api.utils import StrEnum
-from framework.params import (
+from api.params import (
Params,
Switch,
YesNoSwitch,
@@ -24,7 +23,8 @@
modify_str,
str_from_flag_value,
)
-from framework.params.eal import EalParams
+from api.params.eal import EalParams
+from api.utils import StrEnum
class PortTopology(StrEnum):
diff --git a/dts/framework/remote_session/blocking_app.py b/dts/framework/remote_session/blocking_app.py
index 84db3974b1..537d937eca 100644
--- a/dts/framework/remote_session/blocking_app.py
+++ b/dts/framework/remote_session/blocking_app.py
@@ -31,9 +31,9 @@
from typing_extensions import Self
from api.context import get_ctx
+from api.params import Params
+from api.params.eal import EalParams
from api.testbed_model.node import Node
-from framework.params import Params
-from framework.params.eal import EalParams
from framework.remote_session.dpdk_shell import compute_eal_params
from framework.remote_session.interactive_shell import InteractiveShell
diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py
index 713a564d25..afdf7526d9 100644
--- a/dts/framework/remote_session/dpdk.py
+++ b/dts/framework/remote_session/dpdk.py
@@ -15,6 +15,7 @@
from api.context import get_ctx
from api.exception import ConfigurationError, RemoteFileNotFoundError
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from api.testbed_model.node import Node
from api.testbed_model.os_session import OSSession
@@ -32,7 +33,6 @@
RemoteDPDKTreeLocation,
)
from framework.logger import DTSLogger, get_dts_logger
-from framework.params.eal import EalParams
from framework.remote_session.remote_session import CommandResult
diff --git a/dts/framework/remote_session/dpdk_shell.py b/dts/framework/remote_session/dpdk_shell.py
index b807f9bdae..61cc4687f3 100644
--- a/dts/framework/remote_session/dpdk_shell.py
+++ b/dts/framework/remote_session/dpdk_shell.py
@@ -11,8 +11,8 @@
from pathlib import PurePath
from api.context import get_ctx
+from api.params.eal import EalParams
from api.testbed_model.cpu import LogicalCoreList
-from framework.params.eal import EalParams
from framework.remote_session.interactive_shell import (
InteractiveShell,
only_active,
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index ec539bad95..f7f0669eea 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -35,9 +35,9 @@
InteractiveSSHSessionDeadError,
InteractiveSSHTimeoutError,
)
+from api.params import Params
from api.testbed_model.node import Node
from framework.logger import DTSLogger, get_dts_logger
-from framework.params import Params
from framework.settings import SETTINGS
P = ParamSpec("P")
diff --git a/dts/tests/TestSuite_packet_capture.py b/dts/tests/TestSuite_packet_capture.py
index fd5cef5268..ba67c9e1c6 100644
--- a/dts/tests/TestSuite_packet_capture.py
+++ b/dts/tests/TestSuite_packet_capture.py
@@ -35,6 +35,7 @@
match_all_packets,
send_packets_and_capture,
)
+from api.params import Params
from api.test import verify
from api.test_suite import TestSuite, func_test
from api.testbed_model.cpu import LogicalCoreList
@@ -42,7 +43,6 @@
PacketFilteringConfig,
)
from api.testpmd import TestPmd
-from framework.params import Params
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.dpdk_shell import compute_eal_params
diff --git a/dts/tests/TestSuite_single_core_forward_perf.py b/dts/tests/TestSuite_single_core_forward_perf.py
index f1eb435759..24f2cebf17 100644
--- a/dts/tests/TestSuite_single_core_forward_perf.py
+++ b/dts/tests/TestSuite_single_core_forward_perf.py
@@ -21,11 +21,11 @@
requires_link_topology,
)
from api.packet import assess_performance_by_packet
+from api.params.types import TestPmdParamsDict
from api.test import verify, write_performance_json
from api.test_suite import BaseConfig, TestSuite, perf_test
from api.testpmd import TestPmd
from api.testpmd.config import RXRingParams, TXRingParams
-from framework.params.types import TestPmdParamsDict
class Config(BaseConfig):
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v4 7/7] dts: separate Linux session into interface and logic
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
` (5 preceding siblings ...)
2026-04-30 22:06 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
@ 2026-04-30 22:06 ` Dean Marx
6 siblings, 0 replies; 81+ messages in thread
From: Dean Marx @ 2026-04-30 22:06 UTC (permalink / raw)
To: patrickrobb1997, luca.vizzarro, yoan.picchi, Honnappa.Nagarahalli,
paul.szczepanek
Cc: dev, Dean Marx
Separate Linux session into an interface for the API,
and a logical module in the framework.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/framework.linux_session.rst | 8 +
doc/api/dts/index.rst | 1 +
dts/api/testbed_model/linux_session.py | 372 ++----------------------
dts/api/testbed_model/node.py | 6 +-
dts/framework/linux_session.py | 366 +++++++++++++++++++++++
5 files changed, 402 insertions(+), 351 deletions(-)
create mode 100644 doc/api/dts/framework.linux_session.rst
create mode 100644 dts/framework/linux_session.py
diff --git a/doc/api/dts/framework.linux_session.rst b/doc/api/dts/framework.linux_session.rst
new file mode 100644
index 0000000000..9de2e1484d
--- /dev/null
+++ b/doc/api/dts/framework.linux_session.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+framework.linux\_session
+========================
+
+.. automodule:: framework.linux_session
+ :members:
+ :show-inheritance:
diff --git a/doc/api/dts/index.rst b/doc/api/dts/index.rst
index e89e782ac0..0dbc18b75c 100644
--- a/doc/api/dts/index.rst
+++ b/doc/api/dts/index.rst
@@ -37,6 +37,7 @@ Modules
framework.parser
api.utils
api.exception
+ framework.linux_session
Indices and tables
diff --git a/dts/api/testbed_model/linux_session.py b/dts/api/testbed_model/linux_session.py
index 7307b2abe2..5bcbf1ce97 100644
--- a/dts/api/testbed_model/linux_session.py
+++ b/dts/api/testbed_model/linux_session.py
@@ -1,367 +1,41 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2023 PANTHEON.tech s.r.o.
# Copyright(c) 2023 University of New Hampshire
+"""Linux OS session interface.
-"""Linux OS translator.
-
-Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
-compliant with POSIX standards, so this module only implements the parts that aren't.
-This intermediate module implements the common parts of mostly POSIX compliant distributions.
+Extends the base :class:`~.os_session.OSSession` with methods specific to Linux nodes.
+The concrete implementation containing all backend logic lives in the framework package.
"""
-import json
-import re
-from collections.abc import Iterable
-from functools import cached_property
+from abc import ABC, abstractmethod
from pathlib import PurePath
-from typing import TypedDict
-
-from typing_extensions import NotRequired
-
-from api.exception import (
- ConfigurationError,
- InternalError,
- RemoteCommandExecutionError,
-)
-from api.testbed_model.port import PortInfo
-from api.utils import expand_range
-
-from .cpu import LogicalCore
-from .port import Port
-from .posix_session import PosixSession
-
-
-class LshwConfigurationOutput(TypedDict):
- """The relevant parts of ``lshw``'s ``configuration`` section."""
-
- #:
- driver: str
- #:
- link: str
-
-
-class LshwOutput(TypedDict):
- """A model of the relevant information from ``lshw``'s json output.
-
- Example:
- ::
-
- {
- ...
- "businfo" : "pci@0000:08:00.0",
- "logicalname" : "enp8s0",
- "version" : "00",
- "serial" : "52:54:00:59:e1:ac",
- ...
- "configuration" : {
- ...
- "link" : "yes",
- ...
- },
- ...
- """
-
- #:
- businfo: str
- #:
- logicalname: NotRequired[str]
- #:
- serial: NotRequired[str]
- #:
- configuration: LshwConfigurationOutput
-
-
-class LinuxSession(PosixSession):
- """The implementation of non-Posix compliant parts of Linux."""
-
- @staticmethod
- def _get_privileged_command(command: str) -> str:
- command = command.replace(r"'", r"\'")
- return f"sudo -- sh -c '{command}'"
- def get_remote_cpus(self) -> list[LogicalCore]:
- """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
- cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
- lcores = []
- for cpu_line in cpu_info.splitlines():
- lcore, core, socket, node = map(int, cpu_line.split(","))
- lcores.append(LogicalCore(lcore, core, socket, node))
- return lcores
-
- def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
- """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
- return dpdk_prefix
-
- def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
-
- Raises:
- ConfigurationError: If the given `hugepage_size` is not supported by the OS.
- """
- self._logger.info("Getting Hugepage information.")
- if (
- f"hugepages-{hugepage_size}kB"
- not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
- ):
- raise ConfigurationError("hugepage size not supported by operating system")
- hugepages_total = self._get_hugepages_total(hugepage_size)
- self._numa_nodes = self._get_numa_nodes()
-
- if force_first_numa or hugepages_total < number_of:
- # when forcing numa, we need to clear existing hugepages regardless
- # of size, so they can be moved to the first numa node
- self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
- else:
- self._logger.info("Hugepages already configured.")
- self._mount_huge_pages()
-
- def _get_hugepages_total(self, hugepage_size: int) -> int:
- hugepages_total = self.send_command(
- f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
- ).stdout
- return int(hugepages_total)
-
- def _get_numa_nodes(self) -> list[int]:
- try:
- numa_count = self.send_command(
- "cat /sys/devices/system/node/online", verify=True
- ).stdout
- numa_range = expand_range(numa_count)
- except RemoteCommandExecutionError:
- # the file doesn't exist, meaning the node doesn't support numa
- numa_range = []
- return numa_range
-
- def _mount_huge_pages(self) -> None:
- self._logger.info("Re-mounting Hugepages.")
- hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
- self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
- result = self.send_command(hugapge_fs_cmd)
- if result.stdout == "":
- remote_mount_path = "/mnt/huge"
- self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
- self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
-
- def _supports_numa(self) -> bool:
- # the system supports numa if self._numa_nodes is non-empty and there are more
- # than one numa node (in the latter case it may actually support numa, but
- # there's no reason to do any numa specific configuration)
- return len(self._numa_nodes) > 1
-
- def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
- self._logger.info("Configuring Hugepages.")
- hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
- if force_first_numa and self._supports_numa():
- # clear non-numa hugepages
- self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
- hugepage_config_path = (
- f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
- f"/hugepages-{size}kB/nr_hugepages"
- )
-
- self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
-
- def get_port_info(self, pci_address: str) -> PortInfo:
- """Overrides :meth:`~.os_session.OSSession.get_port_info`.
-
- Raises:
- ConfigurationError: If the port could not be found.
- """
- bus_info = f"pci@{pci_address}"
- port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
- if port is None:
- raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
- logical_name = port.get("logicalname", "")
- mac_address = port.get("serial", "")
+class LinuxSession(ABC):
+ """Abstract interface for Linux-specific OS session operations."""
- configuration = port.get("configuration", {})
- driver = configuration.get("driver", "")
- is_link_up = configuration.get("link", "down") == "up"
-
- return PortInfo(mac_address, logical_name, driver, is_link_up)
-
- def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
-
- The :attr:`~.devbind_script_path` property must be setup in order to call this method.
- """
- ports_pci_addrs = " ".join(port.pci for port in ports)
-
- self.send_command(
- f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
- privileged=True,
- verify=True,
- )
-
- del self._lshw_net_info
-
- def bring_up_link(self, ports: Iterable[Port]) -> None:
- """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
- for port in ports:
- self.send_command(
- f"ip link set dev {port.logical_name} up", privileged=True, verify=True
- )
-
- del self._lshw_net_info
-
- def set_interface_link_up(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
- self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
-
- def delete_interface(self, name: str) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
- self.send_command(f"ip link delete {name}", privileged=True)
-
- @cached_property
+ @property
+ @abstractmethod
def devbind_script_path(self) -> PurePath:
- """The path to the dpdk-devbind.py script on the node.
-
- Needs to be manually assigned first in order to be used.
+ """The path to the devbind script."""
- Raises:
- InternalError: If accessed before environment setup.
- """
- raise InternalError("Accessed devbind script path before setup.")
-
- def load_vfio(self, pf_port: Port) -> None:
- """Overrides :meth:`~os_session.OSSession,load_vfio`."""
- cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
- device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
- if device and device.group(1) in ["37c8", "0435", "19e2"]:
- self.send_command(
- "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
- privileged=True,
- )
- self.send_command(
- "modprobe -r vfio_virqfd; modprobe -r vfio",
- privileged=True,
- )
- self.send_command(
- "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
- )
- self.send_command(
- "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
- privileged=True,
- )
- else:
- self.send_command("modprobe vfio-pci")
- self.refresh_lshw()
+ @devbind_script_path.setter
+ @abstractmethod
+ def devbind_script_path(self, value: PurePath) -> None:
+ """Set the devbind script path after environment setup."""
- def create_crypto_vfs(self, pf_port: list[Port]) -> None:
- """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+ @abstractmethod
+ def set_interface_link_up(self, name: str) -> None:
+ """Set the link status of an interface to up.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ name: The name of the interface.
"""
- for port in pf_port:
- self.delete_crypto_vfs(port)
- for port in pf_port:
- sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
- )
- self.send_command(
- f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
- )
-
- self.refresh_lshw()
- def create_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+ @abstractmethod
+ def delete_interface(self, name: str) -> None:
+ """Delete a virtual interface.
- Raises:
- InternalError: If there are existing VFs which have to be deleted.
+ Args:
+ name: The name of the interface to delete.
"""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if 0 < curr_num_vfs:
- raise InternalError("There are existing VFs on the port which must be deleted.")
- if curr_num_vfs == 0:
- self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
- self.refresh_lshw()
-
- def delete_crypto_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
- self.send_command(
- f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
- privileged=True,
- )
- self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
-
- def delete_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(
- self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
- )
- if curr_num_vfs == 0:
- self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
- else:
- self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
-
- def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- f"readlink {sys_bus_path}/virtfn*",
- privileged=True,
- )
- return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
- return []
-
- def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
- """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
- sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
- curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
- if curr_num_vfs > 0:
- pci_addrs = self.send_command(
- 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
- + f"{sys_bus_path}/virtfn*/uevent",
- privileged=True,
- )
- return pci_addrs.stdout.splitlines()
- else:
- return []
-
- @cached_property
- def _lshw_net_info(self) -> list[LshwOutput]:
- output = self.send_command("lshw -quiet -json -C network", verify=True)
- return json.loads(output.stdout)
-
- def refresh_lshw(self) -> None:
- """Force refresh of cached lshw network info."""
- if "_lshw_net_info" in self.__dict__:
- del self.__dict__["_lshw_net_info"]
- _ = self._lshw_net_info
-
- def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
- if attr_value:
- setattr(port, attr_name, attr_value)
- self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
- else:
- self._logger.warning(
- f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
- )
-
- def configure_port_mtu(self, mtu: int, port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
- self.send_command(
- f"ip link set dev {port.logical_name} mtu {mtu}",
- privileged=True,
- verify=True,
- )
-
- def configure_ipv4_forwarding(self, enable: bool) -> None:
- """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
- state = 1 if enable else 0
- self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
diff --git a/dts/api/testbed_model/node.py b/dts/api/testbed_model/node.py
index 40dd7f0666..ca634655e6 100644
--- a/dts/api/testbed_model/node.py
+++ b/dts/api/testbed_model/node.py
@@ -15,17 +15,19 @@
from functools import cached_property
from pathlib import PurePath
-from typing import Literal, TypeAlias
+from typing import TYPE_CHECKING, Literal, TypeAlias
from api.exception import ConfigurationError, InternalError
from framework.config.node import (
OS,
NodeConfiguration,
)
+
+if TYPE_CHECKING:
+ from framework.linux_session import LinuxSession
from framework.logger import DTSLogger, get_dts_logger
from .cpu import Architecture, LogicalCore
-from .linux_session import LinuxSession
from .os_session import OSSession, OSSessionInfo
from .port import Port
diff --git a/dts/framework/linux_session.py b/dts/framework/linux_session.py
new file mode 100644
index 0000000000..e5320b7fc4
--- /dev/null
+++ b/dts/framework/linux_session.py
@@ -0,0 +1,366 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+# Copyright(c) 2023 University of New Hampshire
+
+"""Linux OS translator.
+
+Translate OS-unaware calls into Linux calls/utilities. Most of Linux distributions are mostly
+compliant with POSIX standards, so this module only implements the parts that aren't.
+This intermediate module implements the common parts of mostly POSIX compliant distributions.
+"""
+
+import json
+import re
+from collections.abc import Iterable
+from functools import cached_property
+from pathlib import PurePath
+from typing import TypedDict
+
+from typing_extensions import NotRequired
+
+from api.exception import (
+ ConfigurationError,
+ InternalError,
+ RemoteCommandExecutionError,
+)
+from api.testbed_model.cpu import LogicalCore
+from api.testbed_model.linux_session import LinuxSession as LinuxSessionBase
+from api.testbed_model.port import Port, PortInfo
+from api.testbed_model.posix_session import PosixSession
+from api.utils import expand_range
+
+
+class LshwConfigurationOutput(TypedDict):
+ """The relevant parts of ``lshw``'s ``configuration`` section."""
+
+ #:
+ driver: str
+ #:
+ link: str
+
+
+class LshwOutput(TypedDict):
+ """A model of the relevant information from ``lshw``'s json output.
+
+ Example:
+ ::
+
+ {
+ ...
+ "businfo" : "pci@0000:08:00.0",
+ "logicalname" : "enp8s0",
+ "version" : "00",
+ "serial" : "52:54:00:59:e1:ac",
+ ...
+ "configuration" : {
+ ...
+ "link" : "yes",
+ ...
+ },
+ ...
+ """
+
+ #:
+ businfo: str
+ #:
+ logicalname: NotRequired[str]
+ #:
+ serial: NotRequired[str]
+ #:
+ configuration: LshwConfigurationOutput
+
+
+class LinuxSession(PosixSession, LinuxSessionBase):
+ """The implementation of non-Posix compliant parts of Linux."""
+
+ @staticmethod
+ def _get_privileged_command(command: str) -> str:
+ command = command.replace(r"'", r"\'")
+ return f"sudo -- sh -c '{command}'"
+
+ def get_remote_cpus(self) -> list[LogicalCore]:
+ """Overrides :meth:`~.os_session.OSSession.get_remote_cpus`."""
+ cpu_info = self.send_command("lscpu -p=CPU,CORE,SOCKET,NODE|grep -v \\#").stdout
+ lcores = []
+ for cpu_line in cpu_info.splitlines():
+ lcore, core, socket, node = map(int, cpu_line.split(","))
+ lcores.append(LogicalCore(lcore, core, socket, node))
+ return lcores
+
+ def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
+ """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
+ return dpdk_prefix
+
+ def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.setup_hugepages`.
+
+ Raises:
+ ConfigurationError: If the given `hugepage_size` is not supported by the OS.
+ """
+ self._logger.info("Getting Hugepage information.")
+ if (
+ f"hugepages-{hugepage_size}kB"
+ not in self.send_command("ls /sys/kernel/mm/hugepages").stdout
+ ):
+ raise ConfigurationError("hugepage size not supported by operating system")
+ hugepages_total = self._get_hugepages_total(hugepage_size)
+ self._numa_nodes = self._get_numa_nodes()
+
+ if force_first_numa or hugepages_total < number_of:
+ # when forcing numa, we need to clear existing hugepages regardless
+ # of size, so they can be moved to the first numa node
+ self._configure_huge_pages(number_of, hugepage_size, force_first_numa)
+ else:
+ self._logger.info("Hugepages already configured.")
+ self._mount_huge_pages()
+
+ def _get_hugepages_total(self, hugepage_size: int) -> int:
+ hugepages_total = self.send_command(
+ f"cat /sys/kernel/mm/hugepages/hugepages-{hugepage_size}kB/nr_hugepages"
+ ).stdout
+ return int(hugepages_total)
+
+ def _get_numa_nodes(self) -> list[int]:
+ try:
+ numa_count = self.send_command(
+ "cat /sys/devices/system/node/online", verify=True
+ ).stdout
+ numa_range = expand_range(numa_count)
+ except RemoteCommandExecutionError:
+ # the file doesn't exist, meaning the node doesn't support numa
+ numa_range = []
+ return numa_range
+
+ def _mount_huge_pages(self) -> None:
+ self._logger.info("Re-mounting Hugepages.")
+ hugapge_fs_cmd = "awk '/hugetlbfs/ { print $2 }' /proc/mounts"
+ self.send_command(f"umount $({hugapge_fs_cmd})", privileged=True)
+ result = self.send_command(hugapge_fs_cmd)
+ if result.stdout == "":
+ remote_mount_path = "/mnt/huge"
+ self.send_command(f"mkdir -p {remote_mount_path}", privileged=True)
+ self.send_command(f"mount -t hugetlbfs nodev {remote_mount_path}", privileged=True)
+
+ def _supports_numa(self) -> bool:
+ # the system supports numa if self._numa_nodes is non-empty and there are more
+ # than one numa node (in the latter case it may actually support numa, but
+ # there's no reason to do any numa specific configuration)
+ return len(self._numa_nodes) > 1
+
+ def _configure_huge_pages(self, number_of: int, size: int, force_first_numa: bool) -> None:
+ self._logger.info("Configuring Hugepages.")
+ hugepage_config_path = f"/sys/kernel/mm/hugepages/hugepages-{size}kB/nr_hugepages"
+ if force_first_numa and self._supports_numa():
+ # clear non-numa hugepages
+ self.send_command(f"echo 0 | tee {hugepage_config_path}", privileged=True)
+ hugepage_config_path = (
+ f"/sys/devices/system/node/node{self._numa_nodes[0]}/hugepages"
+ f"/hugepages-{size}kB/nr_hugepages"
+ )
+
+ self.send_command(f"echo {number_of} | tee {hugepage_config_path}", privileged=True)
+
+ def get_port_info(self, pci_address: str) -> PortInfo:
+ """Overrides :meth:`~.os_session.OSSession.get_port_info`.
+
+ Raises:
+ ConfigurationError: If the port could not be found.
+ """
+ bus_info = f"pci@{pci_address}"
+ port = next(port for port in self._lshw_net_info if port.get("businfo") == bus_info)
+ if port is None:
+ raise ConfigurationError(f"Port {pci_address} could not be found on the node.")
+
+ logical_name = port.get("logicalname", "")
+ mac_address = port.get("serial", "")
+
+ configuration = port.get("configuration", {})
+ driver = configuration.get("driver", "")
+ is_link_up = configuration.get("link", "down") == "up"
+
+ return PortInfo(mac_address, logical_name, driver, is_link_up)
+
+ def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
+
+ The :attr:`~.devbind_script_path` property must be setup in order to call this method.
+ """
+ ports_pci_addrs = " ".join(port.pci for port in ports)
+
+ self.send_command(
+ f"{self.devbind_script_path} -b {driver_name} --force {ports_pci_addrs}",
+ privileged=True,
+ verify=True,
+ )
+
+ del self._lshw_net_info
+
+ def bring_up_link(self, ports: Iterable[Port]) -> None:
+ """Overrides :meth:`~.os_session.OSSession.bring_up_link`."""
+ for port in ports:
+ self.send_command(
+ f"ip link set dev {port.logical_name} up", privileged=True, verify=True
+ )
+
+ del self._lshw_net_info
+
+ def set_interface_link_up(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.set_interface_link_up`."""
+ self.send_command(f"ip link set dev {name} up", privileged=True, verify=True)
+
+ def delete_interface(self, name: str) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_interface`."""
+ self.send_command(f"ip link delete {name}", privileged=True)
+
+ @cached_property
+ def devbind_script_path(self) -> PurePath:
+ """The path to the dpdk-devbind.py script on the node.
+
+ Needs to be manually assigned first in order to be used.
+
+ Raises:
+ InternalError: If accessed before environment setup.
+ """
+ raise InternalError("Accessed devbind script path before setup.")
+
+ def load_vfio(self, pf_port: Port) -> None:
+ """Overrides :meth:`~os_session.OSSession,load_vfio`."""
+ cmd_result = self.send_command(f"lspci -nn -s {pf_port.pci}")
+ device = re.search(r":([0-9a-fA-F]{4})\]", cmd_result.stdout)
+ if device and device.group(1) in ["37c8", "0435", "19e2"]:
+ self.send_command(
+ "modprobe -r vfio_iommu_type1; modprobe -r vfio_pci",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe -r vfio_virqfd; modprobe -r vfio",
+ privileged=True,
+ )
+ self.send_command(
+ "modprobe vfio-pci disable_denylist=1 enable_sriov=1", privileged=True
+ )
+ self.send_command(
+ "echo 1 | tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode",
+ privileged=True,
+ )
+ else:
+ self.send_command("modprobe vfio-pci")
+ self.refresh_lshw()
+
+ def create_crypto_vfs(self, pf_port: list[Port]) -> None:
+ """Overrides :meth:`~os_session.OSSession.create_crypto_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ for port in pf_port:
+ self.delete_crypto_vfs(port)
+ for port in pf_port:
+ sys_bus_path = f"/sys/bus/pci/devices/{port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_totalvfs", privileged=True).stdout
+ )
+ self.send_command(
+ f"echo {num_vfs} | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True
+ )
+
+ self.refresh_lshw()
+
+ def create_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.create_vfs`.
+
+ Raises:
+ InternalError: If there are existing VFs which have to be deleted.
+ """
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if 0 < curr_num_vfs:
+ raise InternalError("There are existing VFs on the port which must be deleted.")
+ if curr_num_vfs == 0:
+ self.send_command(f"echo 1 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+ self.refresh_lshw()
+
+ def delete_crypto_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ self.send_command(
+ f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
+ privileged=True,
+ )
+ self.send_command("echo 1 | sudo tee /sys/bus/pci/rescan", privileged=True)
+
+ def delete_vfs(self, pf_port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.delete_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(
+ self.send_command(f"cat {sys_bus_path}/sriov_numvfs", privileged=True).stdout
+ )
+ if curr_num_vfs == 0:
+ self._logger.debug(f"No VFs found on port {pf_port.pci}, skipping deletion")
+ else:
+ self.send_command(f"echo 0 | sudo tee {sys_bus_path}/sriov_numvfs", privileged=True)
+
+ def get_pci_addr_of_crypto_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_crypto_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ f"readlink {sys_bus_path}/virtfn*",
+ privileged=True,
+ )
+ return [pci.replace("../", "") for pci in pci_addrs.stdout.splitlines()]
+ return []
+
+ def get_pci_addr_of_vfs(self, pf_port: Port) -> list[str]:
+ """Overrides :meth:`~.os_session.OSSession.get_pci_addr_of_vfs`."""
+ sys_bus_path = f"/sys/bus/pci/devices/{pf_port.pci}".replace(":", "\\:")
+ curr_num_vfs = int(self.send_command(f"cat {sys_bus_path}/sriov_numvfs").stdout)
+ if curr_num_vfs > 0:
+ pci_addrs = self.send_command(
+ 'awk -F "PCI_SLOT_NAME=" "/PCI_SLOT_NAME=/ {print \\$2}" '
+ + f"{sys_bus_path}/virtfn*/uevent",
+ privileged=True,
+ )
+ return pci_addrs.stdout.splitlines()
+ else:
+ return []
+
+ @cached_property
+ def _lshw_net_info(self) -> list[LshwOutput]:
+ output = self.send_command("lshw -quiet -json -C network", verify=True)
+ return json.loads(output.stdout)
+
+ def refresh_lshw(self) -> None:
+ """Force refresh of cached lshw network info."""
+ if "_lshw_net_info" in self.__dict__:
+ del self.__dict__["_lshw_net_info"]
+ _ = self._lshw_net_info
+
+ def _update_port_attr(self, port: Port, attr_value: str | None, attr_name: str) -> None:
+ if attr_value:
+ setattr(port, attr_name, attr_value)
+ self._logger.debug(f"Found '{attr_name}' of port {port.pci}: '{attr_value}'.")
+ else:
+ self._logger.warning(
+ f"Attempted to get '{attr_name}' of port {port.pci}, but it doesn't exist."
+ )
+
+ def configure_port_mtu(self, mtu: int, port: Port) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_port_mtu`."""
+ self.send_command(
+ f"ip link set dev {port.logical_name} mtu {mtu}",
+ privileged=True,
+ verify=True,
+ )
+
+ def configure_ipv4_forwarding(self, enable: bool) -> None:
+ """Overrides :meth:`~.os_session.OSSession.configure_ipv4_forwarding`."""
+ state = 1 if enable else 0
+ self.send_command(f"sysctl -w net.ipv4.ip_forward={state}", privileged=True)
--
2.52.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
end of thread, other threads:[~2026-04-30 22:07 UTC | newest]
Thread overview: 81+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-25 15:53 [PATCH v1 0/4] dts: add dynamic queue configuration test suite jspewock
2024-06-25 15:53 ` [PATCH v1 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
2024-06-25 15:53 ` [PATCH v1 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
2024-06-25 15:53 ` [PATCH v1 3/4] dts: add dynamic queue test suite jspewock
2024-06-25 15:53 ` [PATCH v1 4/4] dts: add dynamic queue conf to the yaml schema jspewock
2024-07-03 21:58 ` [PATCH v2 0/4] dts: add dynamic queue configuration test suite jspewock
2024-07-03 21:58 ` [PATCH v2 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
2024-07-03 21:58 ` [PATCH v2 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
2024-07-03 21:58 ` [PATCH v2 3/4] dts: add dynamic queue test suite jspewock
2024-07-03 21:58 ` [PATCH v2 4/4] dts: add dynamic queue conf to the yaml schema jspewock
2024-07-24 15:07 ` [PATCH v3 0/4] dts: add dynamic queue configuration test suite jspewock
2024-07-24 15:07 ` [PATCH v3 1/4] dts: add send_packets to test suites and rework packet addressing jspewock
2024-07-26 14:37 ` Nicholas Pratte
2024-07-26 19:00 ` Nicholas Pratte
2024-07-26 19:13 ` Jeremy Spewock
2024-08-29 19:42 ` Nicholas Pratte
2024-07-24 15:07 ` [PATCH v3 2/4] dts: add port queue modification and forwarding stats to testpmd jspewock
2024-07-24 15:07 ` [PATCH v3 3/4] dts: add dynamic queue test suite jspewock
2024-07-24 15:07 ` [PATCH v3 4/4] dts: add dynamic queue conf to the yaml schema jspewock
2024-09-04 15:49 ` [PATCH v4 0/2] dts: add dynamic queue configuration test suite jspewock
2024-09-04 15:49 ` [PATCH v4 1/2] dts: add port queue modification and forwarding stats to testpmd jspewock
2024-09-04 15:49 ` [PATCH v4 2/2] dts: add dynamic queue test suite jspewock
2024-09-25 19:20 ` [PATCH v5 0/2] dts: add dynamic queue configuration " jspewock
2024-09-25 19:20 ` [PATCH v5 1/2] dts: add port queue modification and forwarding stats to testpmd jspewock
2024-09-25 19:20 ` [PATCH v5 2/2] dts: add dynamic queue test suite jspewock
2024-11-05 16:58 ` [PATCH v6 0/2] dts: add dynamic queue configuration " Dean Marx
2024-11-05 16:58 ` [PATCH v6 1/2] dts: add port queue modification and forwarding stats to testpmd Dean Marx
2024-11-18 23:25 ` Patrick Robb
2024-11-18 23:36 ` Patrick Robb
2024-11-05 16:58 ` [PATCH v6 2/2] dts: add dynamic queue test suite Dean Marx
2024-11-18 23:24 ` Patrick Robb
2024-11-18 23:24 ` Patrick Robb
2026-04-23 17:03 ` [PATCH v1 0/8] dts: move test suite imports from framework to API Dean Marx
2026-04-23 17:03 ` [PATCH v1 1/8] dts: move test suite module " Dean Marx
2026-04-23 17:03 ` [PATCH v1 2/8] dts: move testbed model " Dean Marx
2026-04-23 17:03 ` [PATCH v1 3/8] dts: move exception module " Dean Marx
2026-04-23 17:03 ` [PATCH v1 4/8] dts: move utils " Dean Marx
2026-04-23 17:03 ` [PATCH v1 5/8] dts: move context " Dean Marx
2026-04-23 17:03 ` [PATCH v1 6/8] dts: move params directory " Dean Marx
2026-04-23 17:03 ` [PATCH v1 7/8] dts: separate Linux session into interface and logic Dean Marx
2026-04-23 17:03 ` [PATCH v1 8/8] dts: update API rst files for doc build Dean Marx
2026-04-23 19:04 ` [PATCH v2 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-23 19:04 ` [PATCH v2 1/7] dts: move test suite module " Dean Marx
2026-04-23 19:04 ` [PATCH v2 2/7] dts: move testbed model " Dean Marx
2026-04-23 19:04 ` [PATCH v2 3/7] dts: move exception module " Dean Marx
2026-04-23 19:04 ` [PATCH v2 4/7] dts: move utils " Dean Marx
2026-04-23 19:04 ` [PATCH v2 5/7] dts: move context " Dean Marx
2026-04-23 19:04 ` [PATCH v2 6/7] dts: move params directory " Dean Marx
2026-04-23 19:04 ` [PATCH v2 7/7] dts: separate Linux session into interface and logic Dean Marx
2026-04-24 17:01 ` [PATCH v3 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-24 17:01 ` [PATCH v3 1/7] dts: move exception module " Dean Marx
2026-04-24 17:01 ` [PATCH v3 2/7] dts: move utils " Dean Marx
2026-04-24 17:01 ` [PATCH v3 3/7] dts: move context " Dean Marx
2026-04-24 17:01 ` [PATCH v3 4/7] dts: move testbed model " Dean Marx
2026-04-24 17:01 ` [PATCH v3 5/7] dts: move test suite module " Dean Marx
2026-04-24 17:01 ` [PATCH v3 6/7] dts: move params directory " Dean Marx
2026-04-24 17:01 ` [PATCH v3 7/7] dts: separate Linux session into interface and logic Dean Marx
2026-04-28 18:08 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-28 18:08 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
2026-04-28 18:08 ` [PATCH v4 2/7] dts: move utils " Dean Marx
2026-04-28 18:08 ` [PATCH v4 3/7] dts: move context " Dean Marx
2026-04-28 18:08 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
2026-04-28 18:08 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
2026-04-28 18:08 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
2026-04-28 18:08 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
2026-04-30 21:09 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-30 21:09 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
2026-04-30 21:09 ` [PATCH v4 2/7] dts: move utils " Dean Marx
2026-04-30 21:09 ` [PATCH v4 3/7] dts: move context " Dean Marx
2026-04-30 21:09 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
2026-04-30 21:09 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
2026-04-30 21:09 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
2026-04-30 21:09 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
2026-04-30 22:06 ` [PATCH v4 0/7] dts: move test suite imports from framework to API Dean Marx
2026-04-30 22:06 ` [PATCH v4 1/7] dts: move exception module " Dean Marx
2026-04-30 22:06 ` [PATCH v4 2/7] dts: move utils " Dean Marx
2026-04-30 22:06 ` [PATCH v4 3/7] dts: move context " Dean Marx
2026-04-30 22:06 ` [PATCH v4 4/7] dts: move testbed model " Dean Marx
2026-04-30 22:06 ` [PATCH v4 5/7] dts: move test suite module " Dean Marx
2026-04-30 22:06 ` [PATCH v4 6/7] dts: move params directory " Dean Marx
2026-04-30 22:06 ` [PATCH v4 7/7] dts: separate Linux session into interface and logic Dean Marx
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox