@@ -26,12 +26,11 @@ test_runs:
skip_smoke_tests: false # optional
test_suites: # the following test suites will be run in their entirety
- hello_world
+ vdevs: # optional; if removed, vdevs won't be used in the execution
+ - "crypto_openssl"
# The machine running the DPDK test executable
- system_under_test_node:
- node_name: "SUT 1"
- vdevs: # optional; if removed, vdevs won't be used in the test run
- - "crypto_openssl"
- # Traffic generator node to use for this test run
+ system_under_test_node: "SUT 1"
+ # Traffic generator node to use for this execution environment
traffic_generator_node: "TG 1"
nodes:
# Define a system under test node, having two network ports physically
@@ -40,11 +39,6 @@ nodes:
hostname: sut1.change.me.localhost
user: dtsuser
os: linux
- lcores: "" # use all available logical cores (Skips first core)
- memory_channels: 4 # tells DPDK to use 4 memory channels
- hugepages_2mb: # optional; if removed, will use system hugepage configuration
- number_of: 256
- force_first_numa: false
ports:
# sets up the physical link between "SUT 1"@0000:00:08.0 and "TG 1"@0000:00:08.0
- pci: "0000:00:08.0"
@@ -58,6 +52,12 @@ nodes:
os_driver: i40e
peer_node: "TG 1"
peer_pci: "0000:00:08.1"
+ hugepages_2mb: # optional; if removed, will use system hugepage configuration
+ number_of: 256
+ force_first_numa: false
+ dpdk_config:
+ lcores: "" # use all available logical cores (Skips first core)
+ memory_channels: 4 # tells DPDK to use 4 memory channels
# Define a Scapy traffic generator node, having two network ports
# physically connected to the corresponding ports in SUT 1 (the peer node).
- name: "TG 1"
@@ -161,15 +161,23 @@ class NodeConfiguration(FrozenModel):
password: str | None = None
#: The operating system of the :class:`~framework.testbed_model.node.Node`.
os: OS
- #: A comma delimited list of logical cores to use when running DPDK. ```any```, an empty
- #: string or omitting this field means use any core except for the first one. The first core
- #: will only be used if explicitly set.
- lcores: LogicalCores = ""
#: An optional hugepage configuration.
hugepages: HugepageConfiguration | None = Field(None, alias="hugepages_2mb")
#: The ports that can be used in testing.
ports: list[PortConfig] = Field(min_length=1)
+
+class DPDKConfiguration(FrozenModel):
+ """Configuration of the DPDK EAL parameters."""
+
+ #: A comma delimited list of logical cores to use when running DPDK. ```any```, an empty
+ #: string or omitting this field means use any core except for the first one. The first core
+ #: will only be used if explicitly set.
+ lcores: LogicalCores = ""
+
+ #: The number of memory channels to use when running DPDK.
+ memory_channels: int = 1
+
@property
def use_first_core(self) -> bool:
"""Returns :data:`True` if `lcores` explicitly selects the first core."""
@@ -179,8 +187,8 @@ def use_first_core(self) -> bool:
class SutNodeConfiguration(NodeConfiguration):
""":class:`~framework.testbed_model.sut_node.SutNode` specific configuration."""
- #: The number of memory channels to use when running DPDK.
- memory_channels: int = 1
+ #: The runtime configuration for DPDK.
+ dpdk_config: DPDKConfiguration
class TGNodeConfiguration(NodeConfiguration):
@@ -405,15 +413,6 @@ def validate_names(self) -> Self:
return self
-class TestRunSUTNodeConfiguration(FrozenModel):
- """The SUT node configuration of a test run."""
-
- #: The SUT node to use in this test run.
- node_name: str
- #: The names of virtual devices to test.
- vdevs: list[str] = Field(default_factory=list)
-
-
class TestRunConfiguration(FrozenModel):
"""The configuration of a test run.
@@ -431,10 +430,12 @@ class TestRunConfiguration(FrozenModel):
skip_smoke_tests: bool = False
#: The names of test suites and/or test cases to execute.
test_suites: list[TestSuiteConfig] = Field(min_length=1)
- #: The SUT node configuration to use in this test run.
- system_under_test_node: TestRunSUTNodeConfiguration
+ #: The SUT node name to use in this test run.
+ system_under_test_node: str
#: The TG node name to use in this test run.
traffic_generator_node: str
+ #: The names of virtual devices to test.
+ vdevs: list[str] = Field(default_factory=list)
#: The seed to use for pseudo-random generation.
random_seed: int | None = None
@@ -464,12 +465,12 @@ def test_runs_with_nodes(self) -> list[TestRunWithNodesConfiguration]:
test_runs_with_nodes = []
for test_run_no, test_run in enumerate(self.test_runs):
- sut_node_name = test_run.system_under_test_node.node_name
+ sut_node_name = test_run.system_under_test_node
sut_node = next(filter(lambda n: n.name == sut_node_name, self.nodes), None)
assert sut_node is not None, (
f"test_runs.{test_run_no}.sut_node_config.node_name "
- f"({test_run.system_under_test_node.node_name}) is not a valid node name"
+ f"({test_run.system_under_test_node}) is not a valid node name"
)
assert isinstance(sut_node, SutNodeConfiguration), (
f"test_runs.{test_run_no}.sut_node_config.node_name is a valid node name, "
@@ -277,7 +277,7 @@ def _connect_nodes_and_run_test_run(
tg_node = TGNode(tg_node_config)
tg_nodes[tg_node.name] = tg_node
except Exception as e:
- failed_node = test_run_config.system_under_test_node.node_name
+ failed_node = test_run_config.system_under_test_node
if sut_node:
failed_node = test_run_config.traffic_generator_node
self._logger.exception(f"The Creation of node {failed_node} failed.")
@@ -315,9 +315,7 @@ def _run_test_run(
Raises:
ConfigurationError: If the DPDK sources or build is not set up from config or settings.
"""
- self._logger.info(
- f"Running test run with SUT '{test_run_config.system_under_test_node.node_name}'."
- )
+ self._logger.info(f"Running test run with SUT '{test_run_config.system_under_test_node}'.")
test_run_result.ports = sut_node.ports
test_run_result.sut_info = sut_node.node_info
try:
@@ -185,7 +185,6 @@ def __init__(
# sorting by core is needed in case hyperthreading is enabled
self._lcores_to_filter = sorted(lcore_list, key=lambda x: x.core, reverse=not ascending)
- self.filter()
@abstractmethod
def filter(self) -> list[LogicalCore]:
@@ -228,6 +227,8 @@ def filter(self) -> list[LogicalCore]:
Returns:
The filtered cores.
"""
+ if 0 in self._lcores_to_filter:
+ self._lcores_to_filter = self._lcores_to_filter[1:]
sockets_to_filter = self._filter_sockets(self._lcores_to_filter)
filtered_lcores = []
for socket_to_filter in sockets_to_filter:
@@ -356,6 +357,9 @@ def filter(self) -> list[LogicalCore]:
Raises:
ValueError: If the specified lcore filter specifier is invalid.
"""
+ if 0 not in self._filter_specifier.lcore_list:
+ self._lcores_to_filter = self._lcores_to_filter[1:]
+
if not len(self._filter_specifier.lcore_list):
return self._lcores_to_filter
@@ -67,15 +67,12 @@ class LinuxSession(PosixSession):
def _get_privileged_command(command: str) -> str:
return f"sudo -- sh -c '{command}'"
- def get_remote_cpus(self, use_first_core: bool) -> list[LogicalCore]:
+ 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(","))
- if core == 0 and socket == 0 and not use_first_core:
- self._logger.info("Not using the first physical core.")
- continue
lcores.append(LogicalCore(lcore, core, socket, node))
return lcores
@@ -24,14 +24,7 @@
from framework.exception import ConfigurationError
from framework.logger import DTSLogger, get_dts_logger
-from .cpu import (
- Architecture,
- LogicalCore,
- LogicalCoreCount,
- LogicalCoreList,
- LogicalCoreListFilter,
- lcore_filter,
-)
+from .cpu import Architecture, LogicalCore, LogicalCoreCount, LogicalCoreList, lcore_filter
from .linux_session import LinuxSession
from .os_session import OSSession
from .port import Port
@@ -82,24 +75,8 @@ def __init__(self, node_config: NodeConfiguration):
self._logger = get_dts_logger(self.name)
self.main_session = create_session(self.config, self.name, self._logger)
self.arch = Architecture(self.main_session.get_arch_info())
-
self._logger.info(f"Connected to node: {self.name}")
-
self._get_remote_cpus()
- # filter the node lcores according to the test run configuration
- self.lcores = LogicalCoreListFilter(
- self.lcores, LogicalCoreList(self.config.lcores)
- ).filter()
-
- if LogicalCore(lcore=0, core=0, socket=0, node=0) in self.lcores:
- self._logger.info(
- """
- WARNING: First core being used;
- using the first core is considered risky and should only
- be done by advanced users.
- """
- )
-
self._other_sessions = []
self._init_ports()
@@ -188,7 +165,7 @@ def filter_lcores(
def _get_remote_cpus(self) -> None:
"""Scan CPUs in the remote OS and store a list of LogicalCores."""
self._logger.info("Getting CPU information.")
- self.lcores = self.main_session.get_remote_cpus(self.config.use_first_core)
+ self.lcores = self.main_session.get_remote_cpus()
def _setup_hugepages(self) -> None:
"""Setup hugepages on the node.
@@ -445,7 +445,7 @@ def get_dpdk_version(self, version_path: str | PurePath) -> str:
"""
@abstractmethod
- def get_remote_cpus(self, use_first_core: bool) -> list[LogicalCore]:
+ def get_remote_cpus(self) -> list[LogicalCore]:
r"""Get the list of :class:`~.cpu.LogicalCore`\s on the remote node.
Args:
@@ -33,6 +33,7 @@
from framework.remote_session.remote_session import CommandResult
from framework.utils import MesonArgs, TarCompressionFormat
+from .cpu import LogicalCore, LogicalCoreList
from .node import Node
from .os_session import OSSession, OSSessionInfo
from .virtual_device import VirtualDevice
@@ -92,6 +93,17 @@ def __init__(self, node_config: SutNodeConfiguration):
node_config: The SUT node's test run configuration.
"""
super().__init__(node_config)
+ self.lcores = self.filter_lcores(LogicalCoreList(self.config.dpdk_config.lcores))
+ if LogicalCore(lcore=0, core=0, socket=0, node=0) in self.lcores:
+ self._logger.info(
+ """
+ WARNING: First core being used;
+ using the first core is considered risky and should only
+ be done by advanced users.
+ """
+ )
+ else:
+ self._logger.info("Not using first core")
self.virtual_devices = []
self.dpdk_prefix_list = []
self._env_vars = {}
@@ -198,7 +210,7 @@ def set_up_test_run(
dpdk_build_config: The build configuration of DPDK.
"""
super().set_up_test_run(test_run_config, dpdk_build_config)
- for vdev in test_run_config.system_under_test_node.vdevs:
+ for vdev in test_run_config.vdevs:
self.virtual_devices.append(VirtualDevice(vdev))
self._set_up_dpdk(dpdk_build_config)