178534506SJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause 278534506SJuraj Linkeš# Copyright(c) 2010-2014 Intel Corporation 378534506SJuraj Linkeš# Copyright(c) 2023 PANTHEON.tech s.r.o. 488489c05SJeremy Spewock# Copyright(c) 2023 University of New Hampshire 5fd8cd8eeSLuca Vizzarro# Copyright(c) 2024 Arm Limited 678534506SJuraj Linkeš 76ef07151SJuraj Linkeš"""System under test (DPDK + hardware) node. 86ef07151SJuraj Linkeš 96ef07151SJuraj LinkešA system under test (SUT) is the combination of DPDK 106ef07151SJuraj Linkešand the hardware we're testing with DPDK (NICs, crypto and other devices). 116ef07151SJuraj LinkešAn SUT node is where this SUT runs. 126ef07151SJuraj Linkeš""" 136ef07151SJuraj Linkeš 146ef07151SJuraj Linkeš 15680d8a24SJuraj Linkešimport os 16c020b7ceSJuraj Linkešimport time 17c72ff85dSLuca Vizzarrofrom dataclasses import dataclass 18*b935bdc3SLuca Vizzarrofrom pathlib import Path, PurePath 19680d8a24SJuraj Linkeš 2088489c05SJeremy Spewockfrom framework.config import ( 21ecaff610STomáš Ďurovec DPDKBuildConfiguration, 22*b935bdc3SLuca Vizzarro DPDKBuildOptionsConfiguration, 23*b935bdc3SLuca Vizzarro DPDKPrecompiledBuildConfiguration, 24*b935bdc3SLuca Vizzarro DPDKUncompiledBuildConfiguration, 25*b935bdc3SLuca Vizzarro LocalDPDKTarballLocation, 26*b935bdc3SLuca Vizzarro LocalDPDKTreeLocation, 27*b935bdc3SLuca Vizzarro RemoteDPDKTarballLocation, 28*b935bdc3SLuca Vizzarro RemoteDPDKTreeLocation, 29634bed13SJuraj Linkeš SutNodeConfiguration, 30a24f9604SJuraj Linkeš TestRunConfiguration, 3188489c05SJeremy Spewock) 32f9957667STomáš Ďurovecfrom framework.exception import ConfigurationError, RemoteFileNotFoundError 33967fc62bSLuca Vizzarrofrom framework.params.eal import EalParams 342b2f5a8aSLuca Vizzarrofrom framework.remote_session.remote_session import CommandResult 35f9957667STomáš Ďurovecfrom framework.utils import MesonArgs, TarCompressionFormat 36680d8a24SJuraj Linkeš 3778534506SJuraj Linkešfrom .node import Node 38c72ff85dSLuca Vizzarrofrom .os_session import OSSession, OSSessionInfo 39840b1e01SJuraj Linkešfrom .virtual_device import VirtualDevice 4078534506SJuraj Linkeš 4178534506SJuraj Linkeš 42c72ff85dSLuca Vizzarro@dataclass(slots=True, frozen=True) 43c72ff85dSLuca Vizzarroclass DPDKBuildInfo: 44c72ff85dSLuca Vizzarro """Various versions and other information about a DPDK build. 45c72ff85dSLuca Vizzarro 46c72ff85dSLuca Vizzarro Attributes: 47c72ff85dSLuca Vizzarro dpdk_version: The DPDK version that was built. 48c72ff85dSLuca Vizzarro compiler_version: The version of the compiler used to build DPDK. 49c72ff85dSLuca Vizzarro """ 50c72ff85dSLuca Vizzarro 51c72ff85dSLuca Vizzarro dpdk_version: str | None 52c72ff85dSLuca Vizzarro compiler_version: str | None 53c72ff85dSLuca Vizzarro 54c72ff85dSLuca Vizzarro 5578534506SJuraj Linkešclass SutNode(Node): 566ef07151SJuraj Linkeš """The system under test node. 576ef07151SJuraj Linkeš 586ef07151SJuraj Linkeš The SUT node extends :class:`Node` with DPDK specific features: 596ef07151SJuraj Linkeš 60f9957667STomáš Ďurovec * Managing DPDK source tree on the remote SUT, 61f9957667STomáš Ďurovec * Building the DPDK from source or using a pre-built version, 626ef07151SJuraj Linkeš * Gathering of DPDK build info, 636ef07151SJuraj Linkeš * The running of DPDK apps, interactively or one-time execution, 646ef07151SJuraj Linkeš * DPDK apps cleanup. 656ef07151SJuraj Linkeš 66f9957667STomáš Ďurovec Building DPDK from source uses `build` configuration inside `dpdk_build` of configuration. 676ef07151SJuraj Linkeš 686ef07151SJuraj Linkeš Attributes: 69a24f9604SJuraj Linkeš config: The SUT node configuration. 70a24f9604SJuraj Linkeš virtual_devices: The virtual devices used on the node. 7178534506SJuraj Linkeš """ 72680d8a24SJuraj Linkeš 73634bed13SJuraj Linkeš config: SutNodeConfiguration 74a24f9604SJuraj Linkeš virtual_devices: list[VirtualDevice] 75bfad0948SLuca Vizzarro dpdk_prefix_list: list[str] 76bfad0948SLuca Vizzarro dpdk_timestamp: str 77b8bdc4c5SJuraj Linkeš _env_vars: dict 78680d8a24SJuraj Linkeš _remote_tmp_dir: PurePath 79f9957667STomáš Ďurovec __remote_dpdk_tree_path: str | PurePath | None 80f9957667STomáš Ďurovec _remote_dpdk_build_dir: PurePath | None 81680d8a24SJuraj Linkeš _app_compile_timeout: float 82c020b7ceSJuraj Linkeš _dpdk_kill_session: OSSession | None 8388489c05SJeremy Spewock _dpdk_version: str | None 84c72ff85dSLuca Vizzarro _node_info: OSSessionInfo | None 8588489c05SJeremy Spewock _compiler_version: str | None 8668010b2aSJeremy Spewock _path_to_devbind_script: PurePath | None 87f9957667STomáš Ďurovec _ports_bound_to_dpdk: bool 88680d8a24SJuraj Linkeš 89634bed13SJuraj Linkeš def __init__(self, node_config: SutNodeConfiguration): 906ef07151SJuraj Linkeš """Extend the constructor with SUT node specifics. 916ef07151SJuraj Linkeš 926ef07151SJuraj Linkeš Args: 936ef07151SJuraj Linkeš node_config: The SUT node's test run configuration. 946ef07151SJuraj Linkeš """ 95f614e737SJuraj Linkeš super().__init__(node_config) 96a24f9604SJuraj Linkeš self.virtual_devices = [] 97bfad0948SLuca Vizzarro self.dpdk_prefix_list = [] 98b8bdc4c5SJuraj Linkeš self._env_vars = {} 99680d8a24SJuraj Linkeš self._remote_tmp_dir = self.main_session.get_remote_tmp_dir() 100f9957667STomáš Ďurovec self.__remote_dpdk_tree_path = None 101f9957667STomáš Ďurovec self._remote_dpdk_build_dir = None 102680d8a24SJuraj Linkeš self._app_compile_timeout = 90 103c020b7ceSJuraj Linkeš self._dpdk_kill_session = None 104bfad0948SLuca Vizzarro self.dpdk_timestamp = ( 105c020b7ceSJuraj Linkeš f"{str(os.getpid())}_{time.strftime('%Y%m%d%H%M%S', time.localtime())}" 106c020b7ceSJuraj Linkeš ) 10788489c05SJeremy Spewock self._dpdk_version = None 10888489c05SJeremy Spewock self._node_info = None 10988489c05SJeremy Spewock self._compiler_version = None 11068010b2aSJeremy Spewock self._path_to_devbind_script = None 111f9957667STomáš Ďurovec self._ports_bound_to_dpdk = False 112cecfe0aaSJuraj Linkeš self._logger.info(f"Created node: {self.name}") 113680d8a24SJuraj Linkeš 114680d8a24SJuraj Linkeš @property 115f9957667STomáš Ďurovec def _remote_dpdk_tree_path(self) -> str | PurePath: 116f9957667STomáš Ďurovec """The remote DPDK tree path.""" 117f9957667STomáš Ďurovec if self.__remote_dpdk_tree_path: 118f9957667STomáš Ďurovec return self.__remote_dpdk_tree_path 1196ef07151SJuraj Linkeš 120f9957667STomáš Ďurovec self._logger.warning( 121f9957667STomáš Ďurovec "Failed to get remote dpdk tree path because we don't know the " 122f9957667STomáš Ďurovec "location on the SUT node." 123680d8a24SJuraj Linkeš ) 124f9957667STomáš Ďurovec return "" 125680d8a24SJuraj Linkeš 126680d8a24SJuraj Linkeš @property 127f9957667STomáš Ďurovec def remote_dpdk_build_dir(self) -> str | PurePath: 128f9957667STomáš Ďurovec """The remote DPDK build dir path.""" 129f9957667STomáš Ďurovec if self._remote_dpdk_build_dir: 130f9957667STomáš Ďurovec return self._remote_dpdk_build_dir 131f9957667STomáš Ďurovec 132f9957667STomáš Ďurovec self._logger.warning( 133f9957667STomáš Ďurovec "Failed to get remote dpdk build dir because we don't know the " 134f9957667STomáš Ďurovec "location on the SUT node." 135f9957667STomáš Ďurovec ) 136f9957667STomáš Ďurovec return "" 137f9957667STomáš Ďurovec 138f9957667STomáš Ďurovec @property 139f9957667STomáš Ďurovec def dpdk_version(self) -> str | None: 1406ef07151SJuraj Linkeš """Last built DPDK version.""" 141680d8a24SJuraj Linkeš if self._dpdk_version is None: 142f9957667STomáš Ďurovec self._dpdk_version = self.main_session.get_dpdk_version(self._remote_dpdk_tree_path) 143680d8a24SJuraj Linkeš return self._dpdk_version 144680d8a24SJuraj Linkeš 14588489c05SJeremy Spewock @property 146c72ff85dSLuca Vizzarro def node_info(self) -> OSSessionInfo: 1476ef07151SJuraj Linkeš """Additional node information.""" 14888489c05SJeremy Spewock if self._node_info is None: 14988489c05SJeremy Spewock self._node_info = self.main_session.get_node_info() 15088489c05SJeremy Spewock return self._node_info 15188489c05SJeremy Spewock 15288489c05SJeremy Spewock @property 153f9957667STomáš Ďurovec def compiler_version(self) -> str | None: 1546ef07151SJuraj Linkeš """The node's compiler version.""" 15588489c05SJeremy Spewock if self._compiler_version is None: 156f9957667STomáš Ďurovec self._logger.warning("The `compiler_version` is None because a pre-built DPDK is used.") 157f9957667STomáš Ďurovec 15888489c05SJeremy Spewock return self._compiler_version 15988489c05SJeremy Spewock 160f9957667STomáš Ďurovec @compiler_version.setter 161f9957667STomáš Ďurovec def compiler_version(self, value: str) -> None: 162f9957667STomáš Ďurovec """Set the `compiler_version` used on the SUT node. 163f9957667STomáš Ďurovec 164f9957667STomáš Ďurovec Args: 165f9957667STomáš Ďurovec value: The node's compiler version. 166f9957667STomáš Ďurovec """ 167f9957667STomáš Ďurovec self._compiler_version = value 168f9957667STomáš Ďurovec 16968010b2aSJeremy Spewock @property 170f9957667STomáš Ďurovec def path_to_devbind_script(self) -> PurePath | str: 1716ef07151SJuraj Linkeš """The path to the dpdk-devbind.py script on the node.""" 17268010b2aSJeremy Spewock if self._path_to_devbind_script is None: 17368010b2aSJeremy Spewock self._path_to_devbind_script = self.main_session.join_remote_path( 174f9957667STomáš Ďurovec self._remote_dpdk_tree_path, "usertools", "dpdk-devbind.py" 17568010b2aSJeremy Spewock ) 17668010b2aSJeremy Spewock return self._path_to_devbind_script 17768010b2aSJeremy Spewock 178ecaff610STomáš Ďurovec def get_dpdk_build_info(self) -> DPDKBuildInfo: 179ecaff610STomáš Ďurovec """Get additional DPDK build information. 1806ef07151SJuraj Linkeš 1816ef07151SJuraj Linkeš Returns: 182ecaff610STomáš Ďurovec The DPDK build information, 1836ef07151SJuraj Linkeš """ 184ecaff610STomáš Ďurovec return DPDKBuildInfo(dpdk_version=self.dpdk_version, compiler_version=self.compiler_version) 18588489c05SJeremy Spewock 186f9957667STomáš Ďurovec def set_up_test_run( 187*b935bdc3SLuca Vizzarro self, 188*b935bdc3SLuca Vizzarro test_run_config: TestRunConfiguration, 189*b935bdc3SLuca Vizzarro dpdk_build_config: DPDKBuildConfiguration, 190f9957667STomáš Ďurovec ) -> None: 191f9957667STomáš Ďurovec """Extend the test run setup with vdev config and DPDK build set up. 192680d8a24SJuraj Linkeš 193f9957667STomáš Ďurovec This method extends the setup process by configuring virtual devices and preparing the DPDK 194f9957667STomáš Ďurovec environment based on the provided configuration. 1956ef07151SJuraj Linkeš 196a24f9604SJuraj Linkeš Args: 197a24f9604SJuraj Linkeš test_run_config: A test run configuration according to which 198a24f9604SJuraj Linkeš the setup steps will be taken. 199*b935bdc3SLuca Vizzarro dpdk_build_config: The build configuration of DPDK. 200680d8a24SJuraj Linkeš """ 201*b935bdc3SLuca Vizzarro super().set_up_test_run(test_run_config, dpdk_build_config) 202*b935bdc3SLuca Vizzarro for vdev in test_run_config.system_under_test_node.vdevs: 203a24f9604SJuraj Linkeš self.virtual_devices.append(VirtualDevice(vdev)) 204*b935bdc3SLuca Vizzarro self._set_up_dpdk(dpdk_build_config) 205a24f9604SJuraj Linkeš 206a24f9604SJuraj Linkeš def tear_down_test_run(self) -> None: 207f9957667STomáš Ďurovec """Extend the test run teardown with virtual device teardown and DPDK teardown.""" 208a24f9604SJuraj Linkeš super().tear_down_test_run() 209a24f9604SJuraj Linkeš self.virtual_devices = [] 21011b2279aSTomáš Ďurovec self._tear_down_dpdk() 211a24f9604SJuraj Linkeš 212f9957667STomáš Ďurovec def _set_up_dpdk( 213*b935bdc3SLuca Vizzarro self, 214*b935bdc3SLuca Vizzarro dpdk_build_config: DPDKBuildConfiguration, 215f9957667STomáš Ďurovec ) -> None: 216a24f9604SJuraj Linkeš """Set up DPDK the SUT node and bind ports. 217a24f9604SJuraj Linkeš 218f9957667STomáš Ďurovec DPDK setup includes setting all internals needed for the build, the copying of DPDK 219f9957667STomáš Ďurovec sources and then building DPDK or using the exist ones from the `dpdk_location`. The drivers 220f9957667STomáš Ďurovec are bound to those that DPDK needs. 221a24f9604SJuraj Linkeš 222a24f9604SJuraj Linkeš Args: 223*b935bdc3SLuca Vizzarro dpdk_build_config: A DPDK build configuration to test. 224a24f9604SJuraj Linkeš """ 225*b935bdc3SLuca Vizzarro match dpdk_build_config.dpdk_location: 226*b935bdc3SLuca Vizzarro case RemoteDPDKTreeLocation(dpdk_tree=dpdk_tree): 227*b935bdc3SLuca Vizzarro self._set_remote_dpdk_tree_path(dpdk_tree) 228*b935bdc3SLuca Vizzarro case LocalDPDKTreeLocation(dpdk_tree=dpdk_tree): 229*b935bdc3SLuca Vizzarro self._copy_dpdk_tree(dpdk_tree) 230*b935bdc3SLuca Vizzarro case RemoteDPDKTarballLocation(tarball=tarball): 231*b935bdc3SLuca Vizzarro self._validate_remote_dpdk_tarball(tarball) 232*b935bdc3SLuca Vizzarro self._prepare_and_extract_dpdk_tarball(tarball) 233*b935bdc3SLuca Vizzarro case LocalDPDKTarballLocation(tarball=tarball): 234*b935bdc3SLuca Vizzarro remote_tarball = self._copy_dpdk_tarball_to_remote(tarball) 235*b935bdc3SLuca Vizzarro self._prepare_and_extract_dpdk_tarball(remote_tarball) 236f9957667STomáš Ďurovec 237*b935bdc3SLuca Vizzarro match dpdk_build_config: 238*b935bdc3SLuca Vizzarro case DPDKPrecompiledBuildConfiguration(precompiled_build_dir=build_dir): 239*b935bdc3SLuca Vizzarro self._set_remote_dpdk_build_dir(build_dir) 240*b935bdc3SLuca Vizzarro case DPDKUncompiledBuildConfiguration(build_options=build_options): 241*b935bdc3SLuca Vizzarro self._configure_dpdk_build(build_options) 242680d8a24SJuraj Linkeš self._build_dpdk() 243f9957667STomáš Ďurovec 24468010b2aSJeremy Spewock self.bind_ports_to_driver() 24568010b2aSJeremy Spewock 24611b2279aSTomáš Ďurovec def _tear_down_dpdk(self) -> None: 247a24f9604SJuraj Linkeš """Reset DPDK variables and bind port driver to the OS driver.""" 248a24f9604SJuraj Linkeš self._env_vars = {} 249f9957667STomáš Ďurovec self.__remote_dpdk_tree_path = None 250f9957667STomáš Ďurovec self._remote_dpdk_build_dir = None 251a24f9604SJuraj Linkeš self._dpdk_version = None 252f9957667STomáš Ďurovec self.compiler_version = None 25368010b2aSJeremy Spewock self.bind_ports_to_driver(for_dpdk=False) 254680d8a24SJuraj Linkeš 255*b935bdc3SLuca Vizzarro def _set_remote_dpdk_tree_path(self, dpdk_tree: PurePath): 256f9957667STomáš Ďurovec """Set the path to the remote DPDK source tree based on the provided DPDK location. 257f9957667STomáš Ďurovec 258f9957667STomáš Ďurovec Verify DPDK source tree existence on the SUT node, if exists sets the 259f9957667STomáš Ďurovec `_remote_dpdk_tree_path` property, otherwise sets nothing. 260f9957667STomáš Ďurovec 261f9957667STomáš Ďurovec Args: 262f9957667STomáš Ďurovec dpdk_tree: The path to the DPDK source tree directory. 263f9957667STomáš Ďurovec 264f9957667STomáš Ďurovec Raises: 265f9957667STomáš Ďurovec RemoteFileNotFoundError: If the DPDK source tree is expected to be on the SUT node but 266f9957667STomáš Ďurovec is not found. 267f9957667STomáš Ďurovec """ 268f9957667STomáš Ďurovec if not self.main_session.remote_path_exists(dpdk_tree): 269f9957667STomáš Ďurovec raise RemoteFileNotFoundError( 270f9957667STomáš Ďurovec f"Remote DPDK source tree '{dpdk_tree}' not found in SUT node." 271f9957667STomáš Ďurovec ) 272f9957667STomáš Ďurovec if not self.main_session.is_remote_dir(dpdk_tree): 273*b935bdc3SLuca Vizzarro raise ConfigurationError(f"Remote DPDK source tree '{dpdk_tree}' must be a directory.") 274f9957667STomáš Ďurovec 275*b935bdc3SLuca Vizzarro self.__remote_dpdk_tree_path = dpdk_tree 276f9957667STomáš Ďurovec 277*b935bdc3SLuca Vizzarro def _copy_dpdk_tree(self, dpdk_tree_path: Path) -> None: 278f9957667STomáš Ďurovec """Copy the DPDK source tree to the SUT. 279f9957667STomáš Ďurovec 280f9957667STomáš Ďurovec Args: 281f9957667STomáš Ďurovec dpdk_tree_path: The path to DPDK source tree on local filesystem. 282f9957667STomáš Ďurovec """ 283f9957667STomáš Ďurovec self._logger.info( 284f9957667STomáš Ďurovec f"Copying DPDK source tree to SUT: '{dpdk_tree_path}' into '{self._remote_tmp_dir}'." 285f9957667STomáš Ďurovec ) 286f9957667STomáš Ďurovec self.main_session.copy_dir_to( 287f9957667STomáš Ďurovec dpdk_tree_path, 288f9957667STomáš Ďurovec self._remote_tmp_dir, 289f9957667STomáš Ďurovec exclude=[".git", "*.o"], 290f9957667STomáš Ďurovec compress_format=TarCompressionFormat.gzip, 291f9957667STomáš Ďurovec ) 292f9957667STomáš Ďurovec 293f9957667STomáš Ďurovec self.__remote_dpdk_tree_path = self.main_session.join_remote_path( 294f9957667STomáš Ďurovec self._remote_tmp_dir, PurePath(dpdk_tree_path).name 295f9957667STomáš Ďurovec ) 296f9957667STomáš Ďurovec 297*b935bdc3SLuca Vizzarro def _validate_remote_dpdk_tarball(self, dpdk_tarball: PurePath) -> None: 298*b935bdc3SLuca Vizzarro """Validate the DPDK tarball on the SUT node. 299f9957667STomáš Ďurovec 300f9957667STomáš Ďurovec Args: 301*b935bdc3SLuca Vizzarro dpdk_tarball: The path to the DPDK tarball on the SUT node. 302f9957667STomáš Ďurovec 303f9957667STomáš Ďurovec Raises: 304*b935bdc3SLuca Vizzarro RemoteFileNotFoundError: If the `dpdk_tarball` is expected to be on the SUT node but is 305*b935bdc3SLuca Vizzarro not found. 306*b935bdc3SLuca Vizzarro ConfigurationError: If the `dpdk_tarball` is a valid path but not a valid tar archive. 307*b935bdc3SLuca Vizzarro """ 308*b935bdc3SLuca Vizzarro if not self.main_session.remote_path_exists(dpdk_tarball): 309*b935bdc3SLuca Vizzarro raise RemoteFileNotFoundError(f"Remote DPDK tarball '{dpdk_tarball}' not found in SUT.") 310*b935bdc3SLuca Vizzarro if not self.main_session.is_remote_tarfile(dpdk_tarball): 311*b935bdc3SLuca Vizzarro raise ConfigurationError(f"Remote DPDK tarball '{dpdk_tarball}' must be a tar archive.") 312*b935bdc3SLuca Vizzarro 313*b935bdc3SLuca Vizzarro def _copy_dpdk_tarball_to_remote(self, dpdk_tarball: Path) -> PurePath: 314*b935bdc3SLuca Vizzarro """Copy the local DPDK tarball to the SUT node. 315*b935bdc3SLuca Vizzarro 316*b935bdc3SLuca Vizzarro Args: 317*b935bdc3SLuca Vizzarro dpdk_tarball: The local path to the DPDK tarball. 318*b935bdc3SLuca Vizzarro 319*b935bdc3SLuca Vizzarro Returns: 320*b935bdc3SLuca Vizzarro The path of the copied tarball on the SUT node. 321*b935bdc3SLuca Vizzarro """ 322*b935bdc3SLuca Vizzarro self._logger.info( 323*b935bdc3SLuca Vizzarro f"Copying DPDK tarball to SUT: '{dpdk_tarball}' into '{self._remote_tmp_dir}'." 324*b935bdc3SLuca Vizzarro ) 325*b935bdc3SLuca Vizzarro self.main_session.copy_to(dpdk_tarball, self._remote_tmp_dir) 326*b935bdc3SLuca Vizzarro return self.main_session.join_remote_path(self._remote_tmp_dir, dpdk_tarball.name) 327*b935bdc3SLuca Vizzarro 328*b935bdc3SLuca Vizzarro def _prepare_and_extract_dpdk_tarball(self, remote_tarball_path: PurePath) -> None: 329*b935bdc3SLuca Vizzarro """Prepare the remote DPDK tree path and extract the tarball. 330*b935bdc3SLuca Vizzarro 331*b935bdc3SLuca Vizzarro This method extracts the remote tarball and sets the `_remote_dpdk_tree_path` property to 332*b935bdc3SLuca Vizzarro the path of the extracted DPDK tree on the SUT node. 333*b935bdc3SLuca Vizzarro 334*b935bdc3SLuca Vizzarro Args: 335*b935bdc3SLuca Vizzarro remote_tarball_path: The path to the DPDK tarball on the SUT node. 336f9957667STomáš Ďurovec """ 337f9957667STomáš Ďurovec 338f9957667STomáš Ďurovec def remove_tarball_suffix(remote_tarball_path: PurePath) -> PurePath: 339f9957667STomáš Ďurovec """Remove the tarball suffix from the path. 340f9957667STomáš Ďurovec 341f9957667STomáš Ďurovec Args: 342f9957667STomáš Ďurovec remote_tarball_path: The path to the remote tarball. 343f9957667STomáš Ďurovec 344f9957667STomáš Ďurovec Returns: 345f9957667STomáš Ďurovec The path without the tarball suffix. 346f9957667STomáš Ďurovec """ 347f9957667STomáš Ďurovec if len(remote_tarball_path.suffixes) > 1: 348f9957667STomáš Ďurovec if remote_tarball_path.suffixes[-2] == ".tar": 349f9957667STomáš Ďurovec suffixes_to_remove = "".join(remote_tarball_path.suffixes[-2:]) 350f9957667STomáš Ďurovec return PurePath(str(remote_tarball_path).replace(suffixes_to_remove, "")) 351f9957667STomáš Ďurovec return remote_tarball_path.with_suffix("") 352f9957667STomáš Ďurovec 353f9957667STomáš Ďurovec tarball_top_dir = self.main_session.get_tarball_top_dir(remote_tarball_path) 354f9957667STomáš Ďurovec self.__remote_dpdk_tree_path = self.main_session.join_remote_path( 355*b935bdc3SLuca Vizzarro remote_tarball_path.parent, 356f9957667STomáš Ďurovec tarball_top_dir or remove_tarball_suffix(remote_tarball_path), 357f9957667STomáš Ďurovec ) 358f9957667STomáš Ďurovec 359f9957667STomáš Ďurovec self._logger.info( 360f9957667STomáš Ďurovec "Extracting DPDK tarball on SUT: " 361f9957667STomáš Ďurovec f"'{remote_tarball_path}' into '{self._remote_dpdk_tree_path}'." 362f9957667STomáš Ďurovec ) 363f9957667STomáš Ďurovec self.main_session.extract_remote_tarball( 364f9957667STomáš Ďurovec remote_tarball_path, 365f9957667STomáš Ďurovec self._remote_dpdk_tree_path, 366f9957667STomáš Ďurovec ) 367f9957667STomáš Ďurovec 368*b935bdc3SLuca Vizzarro def _set_remote_dpdk_build_dir(self, build_dir: str): 369f9957667STomáš Ďurovec """Set the `remote_dpdk_build_dir` on the SUT. 370f9957667STomáš Ďurovec 371*b935bdc3SLuca Vizzarro Check existence on the SUT node and sets the 372f9957667STomáš Ďurovec `remote_dpdk_build_dir` property by joining the `_remote_dpdk_tree_path` and `build_dir`. 373f9957667STomáš Ďurovec Otherwise, sets nothing. 374f9957667STomáš Ďurovec 375f9957667STomáš Ďurovec Args: 376*b935bdc3SLuca Vizzarro build_dir: DPDK has been pre-built and the build directory is located 377f9957667STomáš Ďurovec in a subdirectory of `dpdk_tree` or `tarball` root directory. 378f9957667STomáš Ďurovec 379f9957667STomáš Ďurovec Raises: 380f9957667STomáš Ďurovec RemoteFileNotFoundError: If the `build_dir` is expected but does not exist on the SUT 381f9957667STomáš Ďurovec node. 382f9957667STomáš Ďurovec """ 383f9957667STomáš Ďurovec remote_dpdk_build_dir = self.main_session.join_remote_path( 384f9957667STomáš Ďurovec self._remote_dpdk_tree_path, build_dir 385f9957667STomáš Ďurovec ) 386f9957667STomáš Ďurovec if not self.main_session.remote_path_exists(remote_dpdk_build_dir): 387f9957667STomáš Ďurovec raise RemoteFileNotFoundError( 388f9957667STomáš Ďurovec f"Remote DPDK build dir '{remote_dpdk_build_dir}' not found in SUT node." 389f9957667STomáš Ďurovec ) 390f9957667STomáš Ďurovec 391f9957667STomáš Ďurovec self._remote_dpdk_build_dir = PurePath(remote_dpdk_build_dir) 392f9957667STomáš Ďurovec 393*b935bdc3SLuca Vizzarro def _configure_dpdk_build(self, dpdk_build_config: DPDKBuildOptionsConfiguration) -> None: 394f9957667STomáš Ďurovec """Populate common environment variables and set the DPDK build related properties. 395f9957667STomáš Ďurovec 396f9957667STomáš Ďurovec This method sets `compiler_version` for additional information and `remote_dpdk_build_dir` 397f9957667STomáš Ďurovec from DPDK build config name. 398f9957667STomáš Ďurovec 399f9957667STomáš Ďurovec Args: 400f9957667STomáš Ďurovec dpdk_build_config: A DPDK build configuration to test. 401f9957667STomáš Ďurovec """ 402b8bdc4c5SJuraj Linkeš self._env_vars = {} 403ecaff610STomáš Ďurovec self._env_vars.update(self.main_session.get_dpdk_build_env_vars(dpdk_build_config.arch)) 404ecaff610STomáš Ďurovec if compiler_wrapper := dpdk_build_config.compiler_wrapper: 405ecaff610STomáš Ďurovec self._env_vars["CC"] = f"'{compiler_wrapper} {dpdk_build_config.compiler.name}'" 406ecaff610STomáš Ďurovec else: 407ecaff610STomáš Ďurovec self._env_vars["CC"] = dpdk_build_config.compiler.name 408680d8a24SJuraj Linkeš 409f9957667STomáš Ďurovec self.compiler_version = self.main_session.get_compiler_version( 410f9957667STomáš Ďurovec dpdk_build_config.compiler.name 411680d8a24SJuraj Linkeš ) 412680d8a24SJuraj Linkeš 413f9957667STomáš Ďurovec self._remote_dpdk_build_dir = self.main_session.join_remote_path( 414f9957667STomáš Ďurovec self._remote_dpdk_tree_path, dpdk_build_config.name 415680d8a24SJuraj Linkeš ) 416680d8a24SJuraj Linkeš 417680d8a24SJuraj Linkeš def _build_dpdk(self) -> None: 4186ef07151SJuraj Linkeš """Build DPDK. 4196ef07151SJuraj Linkeš 420f9957667STomáš Ďurovec Uses the already configured DPDK build configuration. Assumes that the 421f9957667STomáš Ďurovec `_remote_dpdk_tree_path` has already been set on the SUT node. 422680d8a24SJuraj Linkeš """ 423680d8a24SJuraj Linkeš self.main_session.build_dpdk( 424680d8a24SJuraj Linkeš self._env_vars, 425680d8a24SJuraj Linkeš MesonArgs(default_library="static", enable_kmods=True, libdir="lib"), 426f9957667STomáš Ďurovec self._remote_dpdk_tree_path, 427680d8a24SJuraj Linkeš self.remote_dpdk_build_dir, 428680d8a24SJuraj Linkeš ) 429680d8a24SJuraj Linkeš 430680d8a24SJuraj Linkeš def build_dpdk_app(self, app_name: str, **meson_dpdk_args: str | bool) -> PurePath: 4316ef07151SJuraj Linkeš """Build one or all DPDK apps. 4326ef07151SJuraj Linkeš 4336ef07151SJuraj Linkeš Requires DPDK to be already built on the SUT node. 4346ef07151SJuraj Linkeš 4356ef07151SJuraj Linkeš Args: 4366ef07151SJuraj Linkeš app_name: The name of the DPDK app to build. 4376ef07151SJuraj Linkeš When `app_name` is ``all``, build all example apps. 4386ef07151SJuraj Linkeš meson_dpdk_args: The arguments found in ``meson_options.txt`` in root DPDK directory. 4396ef07151SJuraj Linkeš Do not use ``-D`` with them. 4406ef07151SJuraj Linkeš 4416ef07151SJuraj Linkeš Returns: 4426ef07151SJuraj Linkeš The directory path of the built app. If building all apps, return 443680d8a24SJuraj Linkeš the path to the examples directory (where all apps reside). 444680d8a24SJuraj Linkeš """ 445680d8a24SJuraj Linkeš self.main_session.build_dpdk( 446680d8a24SJuraj Linkeš self._env_vars, 447680d8a24SJuraj Linkeš MesonArgs(examples=app_name, **meson_dpdk_args), # type: ignore [arg-type] 448680d8a24SJuraj Linkeš # ^^ https://github.com/python/mypy/issues/11583 449f9957667STomáš Ďurovec self._remote_dpdk_tree_path, 450680d8a24SJuraj Linkeš self.remote_dpdk_build_dir, 451680d8a24SJuraj Linkeš rebuild=True, 452680d8a24SJuraj Linkeš timeout=self._app_compile_timeout, 453680d8a24SJuraj Linkeš ) 454680d8a24SJuraj Linkeš 455680d8a24SJuraj Linkeš if app_name == "all": 456517b4b26SJuraj Linkeš return self.main_session.join_remote_path(self.remote_dpdk_build_dir, "examples") 457680d8a24SJuraj Linkeš return self.main_session.join_remote_path( 458680d8a24SJuraj Linkeš self.remote_dpdk_build_dir, "examples", f"dpdk-{app_name}" 459680d8a24SJuraj Linkeš ) 460c020b7ceSJuraj Linkeš 461c020b7ceSJuraj Linkeš def kill_cleanup_dpdk_apps(self) -> None: 4626ef07151SJuraj Linkeš """Kill all dpdk applications on the SUT, then clean up hugepages.""" 463c020b7ceSJuraj Linkeš if self._dpdk_kill_session and self._dpdk_kill_session.is_alive(): 464c020b7ceSJuraj Linkeš # we can use the session if it exists and responds 465bfad0948SLuca Vizzarro self._dpdk_kill_session.kill_cleanup_dpdk_apps(self.dpdk_prefix_list) 466c020b7ceSJuraj Linkeš else: 467c020b7ceSJuraj Linkeš # otherwise, we need to (re)create it 468c020b7ceSJuraj Linkeš self._dpdk_kill_session = self.create_session("dpdk_kill") 469bfad0948SLuca Vizzarro self.dpdk_prefix_list = [] 470c020b7ceSJuraj Linkeš 471444833c0SJuraj Linkeš def run_dpdk_app( 472fd8cd8eeSLuca Vizzarro self, app_path: PurePath, eal_params: EalParams, timeout: float = 30 473444833c0SJuraj Linkeš ) -> CommandResult: 4746ef07151SJuraj Linkeš """Run DPDK application on the remote node. 4756ef07151SJuraj Linkeš 4766ef07151SJuraj Linkeš The application is not run interactively - the command that starts the application 4776ef07151SJuraj Linkeš is executed and then the call waits for it to finish execution. 4786ef07151SJuraj Linkeš 4796ef07151SJuraj Linkeš Args: 4806ef07151SJuraj Linkeš app_path: The remote path to the DPDK application. 481fd8cd8eeSLuca Vizzarro eal_params: EAL parameters to run the DPDK application with. 4826ef07151SJuraj Linkeš timeout: Wait at most this long in seconds for `command` execution to complete. 4836ef07151SJuraj Linkeš 4846ef07151SJuraj Linkeš Returns: 4856ef07151SJuraj Linkeš The result of the DPDK app execution. 486444833c0SJuraj Linkeš """ 487444833c0SJuraj Linkeš return self.main_session.send_command( 488fd8cd8eeSLuca Vizzarro f"{app_path} {eal_params}", timeout, privileged=True, verify=True 489444833c0SJuraj Linkeš ) 490444833c0SJuraj Linkeš 49168010b2aSJeremy Spewock def bind_ports_to_driver(self, for_dpdk: bool = True) -> None: 49268010b2aSJeremy Spewock """Bind all ports on the SUT to a driver. 49368010b2aSJeremy Spewock 49468010b2aSJeremy Spewock Args: 4956ef07151SJuraj Linkeš for_dpdk: If :data:`True`, binds ports to os_driver_for_dpdk. 4966ef07151SJuraj Linkeš If :data:`False`, binds to os_driver. 49768010b2aSJeremy Spewock """ 498f9957667STomáš Ďurovec if self._ports_bound_to_dpdk == for_dpdk: 499f9957667STomáš Ďurovec return 500f9957667STomáš Ďurovec 50168010b2aSJeremy Spewock for port in self.ports: 50268010b2aSJeremy Spewock driver = port.os_driver_for_dpdk if for_dpdk else port.os_driver 50368010b2aSJeremy Spewock self.main_session.send_command( 50468010b2aSJeremy Spewock f"{self.path_to_devbind_script} -b {driver} --force {port.pci}", 50568010b2aSJeremy Spewock privileged=True, 50668010b2aSJeremy Spewock verify=True, 50768010b2aSJeremy Spewock ) 508f9957667STomáš Ďurovec self._ports_bound_to_dpdk = for_dpdk 509