Reviewed-by: Nicholas Pratte <npra...@iol.unh.edu>

On Fri, Jan 24, 2025 at 6:39 AM Luca Vizzarro <luca.vizza...@arm.com> wrote:
>
> From: Nicholas Pratte <npra...@iol.unh.edu>
>
> Rework 'lcores' and 'memory_channels' into a new 'dpdk_config'
> subsection in an effort to make these attributes SUT specific; the
> traffic generator, more often than not, does not need this information.
> Ideally, if such information is needed, then it will be listed in the
> 'traffic_generator' component in TG Node configuration. Such logic is
> not introduced in this patch, but the framework can be rewritten to do
> so without any implications of extreme effort.
>
> To make this work, use_first_core has been removed from the framework
> entirely in favor of doing this within the LogicalCoreListFilter object.
> Since use_first_core was only ever activated when logical core 0 was
> explicitly defined, core 0 can be removed from the list of total logical
> cores assuming that it was not listed within filter_specifier.
>
> This patch also removes 'vdevs' from 'system_under_test_node' and moves
> it into 'test_runs'.
>
> Bugzilla ID: 1360
>
> Signed-off-by: Nicholas Pratte <npra...@iol.unh.edu>
> Signed-off-by: Luca Vizzarro <luca.vizza...@arm.com>
> Reviewed-by: Paul Szczepanek <paul.szczepa...@arm.com>
> Reviewed-by: Dean Marx <dm...@iol.unh.edu>
> ---
>  dts/conf.yaml                                | 20 +++++-----
>  dts/framework/config/__init__.py             | 39 ++++++++++----------
>  dts/framework/runner.py                      |  6 +--
>  dts/framework/testbed_model/cpu.py           |  6 ++-
>  dts/framework/testbed_model/linux_session.py |  5 +--
>  dts/framework/testbed_model/node.py          | 27 +-------------
>  dts/framework/testbed_model/os_session.py    |  2 +-
>  dts/framework/testbed_model/sut_node.py      | 14 ++++++-
>  8 files changed, 54 insertions(+), 65 deletions(-)
>
> diff --git a/dts/conf.yaml b/dts/conf.yaml
> index c93eedbc94..bc78882d0d 100644
> --- a/dts/conf.yaml
> +++ b/dts/conf.yaml
> @@ -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"
> diff --git a/dts/framework/config/__init__.py 
> b/dts/framework/config/__init__.py
> index 5dfa0cf0d4..2496f48e20 100644
> --- a/dts/framework/config/__init__.py
> +++ b/dts/framework/config/__init__.py
> @@ -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, "
> diff --git a/dts/framework/runner.py b/dts/framework/runner.py
> index 510be1a870..0cdbb07e06 100644
> --- a/dts/framework/runner.py
> +++ b/dts/framework/runner.py
> @@ -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:
> diff --git a/dts/framework/testbed_model/cpu.py 
> b/dts/framework/testbed_model/cpu.py
> index d19fa5d597..b8bc601c22 100644
> --- a/dts/framework/testbed_model/cpu.py
> +++ b/dts/framework/testbed_model/cpu.py
> @@ -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
>
> diff --git a/dts/framework/testbed_model/linux_session.py 
> b/dts/framework/testbed_model/linux_session.py
> index e3732f0827..41e7656b0f 100644
> --- a/dts/framework/testbed_model/linux_session.py
> +++ b/dts/framework/testbed_model/linux_session.py
> @@ -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
>
> diff --git a/dts/framework/testbed_model/node.py 
> b/dts/framework/testbed_model/node.py
> index b08b1cf14d..6c2dfd6185 100644
> --- a/dts/framework/testbed_model/node.py
> +++ b/dts/framework/testbed_model/node.py
> @@ -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.
> diff --git a/dts/framework/testbed_model/os_session.py 
> b/dts/framework/testbed_model/os_session.py
> index fcda9b3de1..e436886692 100644
> --- a/dts/framework/testbed_model/os_session.py
> +++ b/dts/framework/testbed_model/os_session.py
> @@ -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:
> diff --git a/dts/framework/testbed_model/sut_node.py 
> b/dts/framework/testbed_model/sut_node.py
> index 11d4b22089..d8f1f9d452 100644
> --- a/dts/framework/testbed_model/sut_node.py
> +++ b/dts/framework/testbed_model/sut_node.py
> @@ -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)
>
> --
> 2.43.0
>

Reply via email to