DPDK runtime environment has logic in methods for two modes of use, one for TG environments and one for SUT environments. TG nodes utilizing these methods only require a single class attribute that DPDK runtime environment holds. TG nodes using these methods do not require the overhead of a new DPDK runtime evironment.
Lift shared methods into a new abstract class and split the existing runtime class into SUT and TG specific runtime classes. Signed-off-by: Andrew Bailey <abai...@iol.unh.edu> --- dts/framework/context.py | 4 +- dts/framework/remote_session/dpdk.py | 151 ++++++++++++++++++--------- dts/framework/test_run.py | 9 +- 3 files changed, 110 insertions(+), 54 deletions(-) diff --git a/dts/framework/context.py b/dts/framework/context.py index 4360bc8699..086f07e0e9 100644 --- a/dts/framework/context.py +++ b/dts/framework/context.py @@ -15,7 +15,7 @@ from framework.testbed_model.topology import Topology if TYPE_CHECKING: - from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKRuntimeEnvironment + from framework.remote_session.dpdk import DPDKBuildEnvironment, DPDKSUTRuntimeEnvironment from framework.testbed_model.traffic_generator.traffic_generator import TrafficGenerator P = ParamSpec("P") @@ -68,7 +68,7 @@ class Context: tg_node: Node topology: Topology dpdk_build: "DPDKBuildEnvironment" - dpdk: "DPDKRuntimeEnvironment" + dpdk: "DPDKSUTRuntimeEnvironment" tg: "TrafficGenerator" local: LocalContext = field(default_factory=LocalContext) shell_pool: ShellPool = field(default_factory=ShellPool) diff --git a/dts/framework/remote_session/dpdk.py b/dts/framework/remote_session/dpdk.py index 606d6e22fe..8e45da4bd5 100644 --- a/dts/framework/remote_session/dpdk.py +++ b/dts/framework/remote_session/dpdk.py @@ -7,6 +7,7 @@ import os import time +from abc import ABC, abstractmethod from collections.abc import Iterable from dataclasses import dataclass from functools import cached_property @@ -302,14 +303,83 @@ def get_dpdk_build_info(self) -> DPDKBuildInfo: return DPDKBuildInfo(dpdk_version=self.dpdk_version, compiler_version=self.compiler_version) -class DPDKRuntimeEnvironment: - """Class handling a DPDK runtime environment.""" +class DPDKRuntimeEnvironment(ABC): + """Shared methods between TG and SUT runtimes.""" - config: Final[DPDKRuntimeConfiguration] - build: Final[DPDKBuildEnvironment | None] _node: Final[Node] + + def __init__(self, node: Node): + """DPDK runtime environment constructor. + + Args: + node: The target node to manage a DPDK environment. + """ + self._node = node + + @abstractmethod + def _prepare_devbind_script(self): + """Prepare the devbind script. + + This script is only available for Linux, if the detected session is not Linux then do + nothing. + + Raises: + InternalError: If dpdk-devbind.py could not be found. + """ + + @abstractmethod + def setup(self) -> None: + """Set up the DPDK runtime on the target node.""" + + +class DPDKTGRuntimeEnvironment(DPDKRuntimeEnvironment): + """Class handling a DPDK runtime environment for a TG.""" + + def __init__(self, node: Node): + """DPDK TG environment constructor. + + Args: + node: The target node to manage a DPDK environment. + """ + super().__init__(node) + + def setup(self) -> None: + """Set up the DPDK runtime on the target node.""" + self._prepare_devbind_script() + + def _prepare_devbind_script(self) -> None: + """Prepare the devbind script. + + Copy the script build from the local repository. + + This script is only available for Linux, if the detected session is not Linux then do + nothing. + + Raises: + InternalError: If dpdk-devbind.py could not be found. + """ + if not isinstance(self._node.main_session, LinuxSession): + return + + local_script_path = Path("..", "usertools", "dpdk-devbind.py").resolve() + if not local_script_path.exists(): + raise InternalError("Could not find dpdk-devbind.py locally.") + + devbind_script_path = self._node.main_session.join_remote_path( + self._node.tmp_dir, local_script_path.name + ) + + self._node.main_session.copy_to(local_script_path, devbind_script_path) + self._node.main_session.devbind_script_path = devbind_script_path + + +class DPDKSUTRuntimeEnvironment(DPDKRuntimeEnvironment): + """Class handling a DPDK runtime environment for a SUT.""" + + config: Final[DPDKRuntimeConfiguration] _logger: Final[DTSLogger] + build: Final[DPDKBuildEnvironment] timestamp: Final[str] _virtual_devices: list[VirtualDevice] _lcores: list[LogicalCore] @@ -322,18 +392,18 @@ def __init__( self, config: DPDKRuntimeConfiguration, node: Node, - build_env: DPDKBuildEnvironment | None = None, + build_env: DPDKBuildEnvironment, ): - """DPDK environment constructor. + """DPDK SUT environment constructor. Args: config: The configuration of DPDK. node: The target node to manage a DPDK environment. - build_env: The DPDK build environment, if any. + build_env: The DPDK build environment. """ - self.config = config + super().__init__(node) self.build = build_env - self._node = node + self.config = config self._logger = get_dts_logger() self.timestamp = f"{str(os.getpid())}_{time.strftime('%Y%m%d%H%M%S', time.localtime())}" @@ -354,17 +424,11 @@ def __init__( self._ports_bound_to_dpdk = False self._kill_session = None - def setup(self): + def setup(self) -> None: """Set up the DPDK runtime on the target node.""" - if self.build: - self.build.setup() + self.build.setup() self._prepare_devbind_script() - def teardown(self) -> None: - """Reset DPDK variables and bind port driver to the OS driver.""" - if self.build: - self.build.teardown() - def run_dpdk_app( self, app_path: PurePath, eal_params: EalParams, timeout: float = 30 ) -> CommandResult: @@ -385,38 +449,6 @@ def run_dpdk_app( f"{app_path} {eal_params}", timeout, privileged=True, verify=True ) - def _prepare_devbind_script(self) -> None: - """Prepare the devbind script. - - If the environment has a build associated with it, then use the script within that build's - tree. Otherwise, copy the script from the local repository. - - This script is only available for Linux, if the detected session is not Linux then do - nothing. - - Raises: - InternalError: If dpdk-devbind.py could not be found. - """ - if not isinstance(self._node.main_session, LinuxSession): - return - - if self.build: - devbind_script_path = self._node.main_session.join_remote_path( - self.build.remote_dpdk_tree_path, "usertools", "dpdk-devbind.py" - ) - else: - local_script_path = Path("..", "usertools", "dpdk-devbind.py").resolve() - if not local_script_path.exists(): - raise InternalError("Could not find dpdk-devbind.py locally.") - - devbind_script_path = self._node.main_session.join_remote_path( - self._node.tmp_dir, local_script_path.name - ) - - self._node.main_session.copy_to(local_script_path, devbind_script_path) - - self._node.main_session.devbind_script_path = devbind_script_path - def filter_lcores( self, filter_specifier: LogicalCoreCount | LogicalCoreList, @@ -459,3 +491,24 @@ def kill_cleanup_dpdk_apps(self) -> None: def get_virtual_devices(self) -> Iterable[VirtualDevice]: """The available DPDK virtual devices.""" return (v for v in self._virtual_devices) + + def _prepare_devbind_script(self) -> None: + """Prepare the devbind script. + + Use the script within the associated build's tree. + + This script is only available for Linux, if the detected session is not Linux then do + nothing. + """ + if not isinstance(self._node.main_session, LinuxSession): + return + + devbind_script_path = self._node.main_session.join_remote_path( + self.build.remote_dpdk_tree_path, "usertools", "dpdk-devbind.py" + ) + + self._node.main_session.devbind_script_path = devbind_script_path + + def teardown(self) -> None: + """Reset DPDK variables and bind port driver to the OS driver.""" + self.build.teardown() diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py index 4355aeeb4b..274dceebfd 100644 --- a/dts/framework/test_run.py +++ b/dts/framework/test_run.py @@ -111,7 +111,10 @@ 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.remote_session.dpdk import ( + DPDKBuildEnvironment, + DPDKSUTRuntimeEnvironment, +) from framework.settings import SETTINGS from framework.test_result import Result, ResultNode, TestRunResult from framework.test_suite import BaseConfig, TestCase, TestSuite @@ -199,11 +202,11 @@ def __init__( ) dpdk_build_env = DPDKBuildEnvironment(config.dpdk.build, sut_node) - dpdk_runtime_env = DPDKRuntimeEnvironment(config.dpdk, sut_node, dpdk_build_env) + dpdk_sut_runtime_env = DPDKSUTRuntimeEnvironment(config.dpdk, sut_node, dpdk_build_env) traffic_generator = create_traffic_generator(config.traffic_generator, tg_node) self.ctx = Context( - sut_node, tg_node, topology, dpdk_build_env, dpdk_runtime_env, traffic_generator + sut_node, tg_node, topology, dpdk_build_env, dpdk_sut_runtime_env, traffic_generator ) self.result = result self.selected_tests = list(self.config.filter_tests(tests_config)) -- 2.50.1