xref: /dpdk/dts/framework/testbed_model/sut_node.py (revision cecfe0aabf585fe49548e19b3b5d7ed98f45783b)
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
578534506SJuraj Linkeš
6680d8a24SJuraj Linkešimport os
7680d8a24SJuraj Linkešimport tarfile
8c020b7ceSJuraj Linkešimport time
9680d8a24SJuraj Linkešfrom pathlib import PurePath
1088489c05SJeremy Spewockfrom typing import Type
11680d8a24SJuraj Linkeš
1288489c05SJeremy Spewockfrom framework.config import (
1388489c05SJeremy Spewock    BuildTargetConfiguration,
1488489c05SJeremy Spewock    BuildTargetInfo,
1588489c05SJeremy Spewock    NodeInfo,
16634bed13SJuraj Linkeš    SutNodeConfiguration,
1788489c05SJeremy Spewock)
1888489c05SJeremy Spewockfrom framework.remote_session import CommandResult, InteractiveShellType, OSSession
19680d8a24SJuraj Linkešfrom framework.settings import SETTINGS
20b8bdc4c5SJuraj Linkešfrom framework.utils import MesonArgs
21680d8a24SJuraj Linkeš
22c020b7ceSJuraj Linkešfrom .hw import LogicalCoreCount, LogicalCoreList, VirtualDevice
2378534506SJuraj Linkešfrom .node import Node
2478534506SJuraj Linkeš
2578534506SJuraj Linkeš
2688489c05SJeremy Spewockclass EalParameters(object):
2788489c05SJeremy Spewock    def __init__(
2888489c05SJeremy Spewock        self,
2988489c05SJeremy Spewock        lcore_list: LogicalCoreList,
3088489c05SJeremy Spewock        memory_channels: int,
3188489c05SJeremy Spewock        prefix: str,
3288489c05SJeremy Spewock        no_pci: bool,
3388489c05SJeremy Spewock        vdevs: list[VirtualDevice],
3488489c05SJeremy Spewock        other_eal_param: str,
3588489c05SJeremy Spewock    ):
3688489c05SJeremy Spewock        """
3788489c05SJeremy Spewock        Generate eal parameters character string;
3888489c05SJeremy Spewock        :param lcore_list: the list of logical cores to use.
3988489c05SJeremy Spewock        :param memory_channels: the number of memory channels to use.
4088489c05SJeremy Spewock        :param prefix: set file prefix string, eg:
4188489c05SJeremy Spewock                        prefix='vf'
4288489c05SJeremy Spewock        :param no_pci: switch of disable PCI bus eg:
4388489c05SJeremy Spewock                        no_pci=True
4488489c05SJeremy Spewock        :param vdevs: virtual device list, eg:
4588489c05SJeremy Spewock                        vdevs=[
4688489c05SJeremy Spewock                            VirtualDevice('net_ring0'),
4788489c05SJeremy Spewock                            VirtualDevice('net_ring1')
4888489c05SJeremy Spewock                        ]
4988489c05SJeremy Spewock        :param other_eal_param: user defined DPDK eal parameters, eg:
5088489c05SJeremy Spewock                        other_eal_param='--single-file-segments'
5188489c05SJeremy Spewock        """
5288489c05SJeremy Spewock        self._lcore_list = f"-l {lcore_list}"
5388489c05SJeremy Spewock        self._memory_channels = f"-n {memory_channels}"
5488489c05SJeremy Spewock        self._prefix = prefix
5588489c05SJeremy Spewock        if prefix:
5688489c05SJeremy Spewock            self._prefix = f"--file-prefix={prefix}"
5788489c05SJeremy Spewock        self._no_pci = "--no-pci" if no_pci else ""
5888489c05SJeremy Spewock        self._vdevs = " ".join(f"--vdev {vdev}" for vdev in vdevs)
5988489c05SJeremy Spewock        self._other_eal_param = other_eal_param
6088489c05SJeremy Spewock
6188489c05SJeremy Spewock    def __str__(self) -> str:
6288489c05SJeremy Spewock        return (
6388489c05SJeremy Spewock            f"{self._lcore_list} "
6488489c05SJeremy Spewock            f"{self._memory_channels} "
6588489c05SJeremy Spewock            f"{self._prefix} "
6688489c05SJeremy Spewock            f"{self._no_pci} "
6788489c05SJeremy Spewock            f"{self._vdevs} "
6888489c05SJeremy Spewock            f"{self._other_eal_param}"
6988489c05SJeremy Spewock        )
7088489c05SJeremy Spewock
7188489c05SJeremy Spewock
7278534506SJuraj Linkešclass SutNode(Node):
7378534506SJuraj Linkeš    """
7478534506SJuraj Linkeš    A class for managing connections to the System under Test, providing
7578534506SJuraj Linkeš    methods that retrieve the necessary information about the node (such as
7678534506SJuraj Linkeš    CPU, memory and NIC details) and configuration capabilities.
77680d8a24SJuraj Linkeš    Another key capability is building DPDK according to given build target.
7878534506SJuraj Linkeš    """
79680d8a24SJuraj Linkeš
80634bed13SJuraj Linkeš    config: SutNodeConfiguration
81c020b7ceSJuraj Linkeš    _dpdk_prefix_list: list[str]
82c020b7ceSJuraj Linkeš    _dpdk_timestamp: str
83680d8a24SJuraj Linkeš    _build_target_config: BuildTargetConfiguration | None
84b8bdc4c5SJuraj Linkeš    _env_vars: dict
85680d8a24SJuraj Linkeš    _remote_tmp_dir: PurePath
86680d8a24SJuraj Linkeš    __remote_dpdk_dir: PurePath | None
87680d8a24SJuraj Linkeš    _app_compile_timeout: float
88c020b7ceSJuraj Linkeš    _dpdk_kill_session: OSSession | None
8988489c05SJeremy Spewock    _dpdk_version: str | None
9088489c05SJeremy Spewock    _node_info: NodeInfo | None
9188489c05SJeremy Spewock    _compiler_version: str | None
92680d8a24SJuraj Linkeš
93634bed13SJuraj Linkeš    def __init__(self, node_config: SutNodeConfiguration):
94680d8a24SJuraj Linkeš        super(SutNode, self).__init__(node_config)
95c020b7ceSJuraj Linkeš        self._dpdk_prefix_list = []
96680d8a24SJuraj Linkeš        self._build_target_config = None
97b8bdc4c5SJuraj Linkeš        self._env_vars = {}
98680d8a24SJuraj Linkeš        self._remote_tmp_dir = self.main_session.get_remote_tmp_dir()
99680d8a24SJuraj Linkeš        self.__remote_dpdk_dir = None
100680d8a24SJuraj Linkeš        self._app_compile_timeout = 90
101c020b7ceSJuraj Linkeš        self._dpdk_kill_session = None
102c020b7ceSJuraj Linkeš        self._dpdk_timestamp = (
103c020b7ceSJuraj Linkeš            f"{str(os.getpid())}_{time.strftime('%Y%m%d%H%M%S', time.localtime())}"
104c020b7ceSJuraj Linkeš        )
10588489c05SJeremy Spewock        self._dpdk_version = None
10688489c05SJeremy Spewock        self._node_info = None
10788489c05SJeremy Spewock        self._compiler_version = None
108*cecfe0aaSJuraj Linkeš        self._logger.info(f"Created node: {self.name}")
109680d8a24SJuraj Linkeš
110680d8a24SJuraj Linkeš    @property
111680d8a24SJuraj Linkeš    def _remote_dpdk_dir(self) -> PurePath:
112680d8a24SJuraj Linkeš        if self.__remote_dpdk_dir is None:
113680d8a24SJuraj Linkeš            self.__remote_dpdk_dir = self._guess_dpdk_remote_dir()
114680d8a24SJuraj Linkeš        return self.__remote_dpdk_dir
115680d8a24SJuraj Linkeš
116680d8a24SJuraj Linkeš    @_remote_dpdk_dir.setter
117680d8a24SJuraj Linkeš    def _remote_dpdk_dir(self, value: PurePath) -> None:
118680d8a24SJuraj Linkeš        self.__remote_dpdk_dir = value
119680d8a24SJuraj Linkeš
120680d8a24SJuraj Linkeš    @property
121680d8a24SJuraj Linkeš    def remote_dpdk_build_dir(self) -> PurePath:
122680d8a24SJuraj Linkeš        if self._build_target_config:
123680d8a24SJuraj Linkeš            return self.main_session.join_remote_path(
124680d8a24SJuraj Linkeš                self._remote_dpdk_dir, self._build_target_config.name
125680d8a24SJuraj Linkeš            )
126680d8a24SJuraj Linkeš        else:
127680d8a24SJuraj Linkeš            return self.main_session.join_remote_path(self._remote_dpdk_dir, "build")
128680d8a24SJuraj Linkeš
129680d8a24SJuraj Linkeš    @property
130680d8a24SJuraj Linkeš    def dpdk_version(self) -> str:
131680d8a24SJuraj Linkeš        if self._dpdk_version is None:
132680d8a24SJuraj Linkeš            self._dpdk_version = self.main_session.get_dpdk_version(
133680d8a24SJuraj Linkeš                self._remote_dpdk_dir
134680d8a24SJuraj Linkeš            )
135680d8a24SJuraj Linkeš        return self._dpdk_version
136680d8a24SJuraj Linkeš
13788489c05SJeremy Spewock    @property
13888489c05SJeremy Spewock    def node_info(self) -> NodeInfo:
13988489c05SJeremy Spewock        if self._node_info is None:
14088489c05SJeremy Spewock            self._node_info = self.main_session.get_node_info()
14188489c05SJeremy Spewock        return self._node_info
14288489c05SJeremy Spewock
14388489c05SJeremy Spewock    @property
14488489c05SJeremy Spewock    def compiler_version(self) -> str:
14588489c05SJeremy Spewock        if self._compiler_version is None:
14688489c05SJeremy Spewock            if self._build_target_config is not None:
14788489c05SJeremy Spewock                self._compiler_version = self.main_session.get_compiler_version(
14888489c05SJeremy Spewock                    self._build_target_config.compiler.name
14988489c05SJeremy Spewock                )
15088489c05SJeremy Spewock            else:
15188489c05SJeremy Spewock                self._logger.warning(
15288489c05SJeremy Spewock                    "Failed to get compiler version because"
15388489c05SJeremy Spewock                    "_build_target_config is None."
15488489c05SJeremy Spewock                )
15588489c05SJeremy Spewock                return ""
15688489c05SJeremy Spewock        return self._compiler_version
15788489c05SJeremy Spewock
15888489c05SJeremy Spewock    def get_build_target_info(self) -> BuildTargetInfo:
15988489c05SJeremy Spewock        return BuildTargetInfo(
16088489c05SJeremy Spewock            dpdk_version=self.dpdk_version, compiler_version=self.compiler_version
16188489c05SJeremy Spewock        )
16288489c05SJeremy Spewock
163680d8a24SJuraj Linkeš    def _guess_dpdk_remote_dir(self) -> PurePath:
164680d8a24SJuraj Linkeš        return self.main_session.guess_dpdk_remote_dir(self._remote_tmp_dir)
165680d8a24SJuraj Linkeš
166680d8a24SJuraj Linkeš    def _set_up_build_target(
167680d8a24SJuraj Linkeš        self, build_target_config: BuildTargetConfiguration
168680d8a24SJuraj Linkeš    ) -> None:
169680d8a24SJuraj Linkeš        """
170680d8a24SJuraj Linkeš        Setup DPDK on the SUT node.
171680d8a24SJuraj Linkeš        """
17288489c05SJeremy Spewock        # we want to ensure that dpdk_version and compiler_version is reset for new
17388489c05SJeremy Spewock        # build targets
17488489c05SJeremy Spewock        self._dpdk_version = None
17588489c05SJeremy Spewock        self._compiler_version = None
176680d8a24SJuraj Linkeš        self._configure_build_target(build_target_config)
177680d8a24SJuraj Linkeš        self._copy_dpdk_tarball()
178680d8a24SJuraj Linkeš        self._build_dpdk()
179680d8a24SJuraj Linkeš
180680d8a24SJuraj Linkeš    def _configure_build_target(
181680d8a24SJuraj Linkeš        self, build_target_config: BuildTargetConfiguration
182680d8a24SJuraj Linkeš    ) -> None:
183680d8a24SJuraj Linkeš        """
184680d8a24SJuraj Linkeš        Populate common environment variables and set build target config.
185680d8a24SJuraj Linkeš        """
186b8bdc4c5SJuraj Linkeš        self._env_vars = {}
187680d8a24SJuraj Linkeš        self._build_target_config = build_target_config
188680d8a24SJuraj Linkeš        self._env_vars.update(
189680d8a24SJuraj Linkeš            self.main_session.get_dpdk_build_env_vars(build_target_config.arch)
190680d8a24SJuraj Linkeš        )
191680d8a24SJuraj Linkeš        self._env_vars["CC"] = build_target_config.compiler.name
192680d8a24SJuraj Linkeš        if build_target_config.compiler_wrapper:
193680d8a24SJuraj Linkeš            self._env_vars["CC"] = (
194680d8a24SJuraj Linkeš                f"'{build_target_config.compiler_wrapper} "
195680d8a24SJuraj Linkeš                f"{build_target_config.compiler.name}'"
196680d8a24SJuraj Linkeš            )
197680d8a24SJuraj Linkeš
198680d8a24SJuraj Linkeš    @Node.skip_setup
199680d8a24SJuraj Linkeš    def _copy_dpdk_tarball(self) -> None:
200680d8a24SJuraj Linkeš        """
201680d8a24SJuraj Linkeš        Copy to and extract DPDK tarball on the SUT node.
202680d8a24SJuraj Linkeš        """
203680d8a24SJuraj Linkeš        self._logger.info("Copying DPDK tarball to SUT.")
204b8bdc4c5SJuraj Linkeš        self.main_session.copy_to(SETTINGS.dpdk_tarball_path, self._remote_tmp_dir)
205680d8a24SJuraj Linkeš
206680d8a24SJuraj Linkeš        # construct remote tarball path
207680d8a24SJuraj Linkeš        # the basename is the same on local host and on remote Node
208680d8a24SJuraj Linkeš        remote_tarball_path = self.main_session.join_remote_path(
209680d8a24SJuraj Linkeš            self._remote_tmp_dir, os.path.basename(SETTINGS.dpdk_tarball_path)
210680d8a24SJuraj Linkeš        )
211680d8a24SJuraj Linkeš
212680d8a24SJuraj Linkeš        # construct remote path after extracting
213680d8a24SJuraj Linkeš        with tarfile.open(SETTINGS.dpdk_tarball_path) as dpdk_tar:
214680d8a24SJuraj Linkeš            dpdk_top_dir = dpdk_tar.getnames()[0]
215680d8a24SJuraj Linkeš        self._remote_dpdk_dir = self.main_session.join_remote_path(
216680d8a24SJuraj Linkeš            self._remote_tmp_dir, dpdk_top_dir
217680d8a24SJuraj Linkeš        )
218680d8a24SJuraj Linkeš
219680d8a24SJuraj Linkeš        self._logger.info(
220680d8a24SJuraj Linkeš            f"Extracting DPDK tarball on SUT: "
221680d8a24SJuraj Linkeš            f"'{remote_tarball_path}' into '{self._remote_dpdk_dir}'."
222680d8a24SJuraj Linkeš        )
223680d8a24SJuraj Linkeš        # clean remote path where we're extracting
224680d8a24SJuraj Linkeš        self.main_session.remove_remote_dir(self._remote_dpdk_dir)
225680d8a24SJuraj Linkeš
226680d8a24SJuraj Linkeš        # then extract to remote path
227680d8a24SJuraj Linkeš        self.main_session.extract_remote_tarball(
228680d8a24SJuraj Linkeš            remote_tarball_path, self._remote_dpdk_dir
229680d8a24SJuraj Linkeš        )
230680d8a24SJuraj Linkeš
231680d8a24SJuraj Linkeš    @Node.skip_setup
232680d8a24SJuraj Linkeš    def _build_dpdk(self) -> None:
233680d8a24SJuraj Linkeš        """
234680d8a24SJuraj Linkeš        Build DPDK. Uses the already configured target. Assumes that the tarball has
235680d8a24SJuraj Linkeš        already been copied to and extracted on the SUT node.
236680d8a24SJuraj Linkeš        """
237680d8a24SJuraj Linkeš        self.main_session.build_dpdk(
238680d8a24SJuraj Linkeš            self._env_vars,
239680d8a24SJuraj Linkeš            MesonArgs(default_library="static", enable_kmods=True, libdir="lib"),
240680d8a24SJuraj Linkeš            self._remote_dpdk_dir,
241680d8a24SJuraj Linkeš            self.remote_dpdk_build_dir,
242680d8a24SJuraj Linkeš        )
243680d8a24SJuraj Linkeš
244680d8a24SJuraj Linkeš    def build_dpdk_app(self, app_name: str, **meson_dpdk_args: str | bool) -> PurePath:
245680d8a24SJuraj Linkeš        """
246680d8a24SJuraj Linkeš        Build one or all DPDK apps. Requires DPDK to be already built on the SUT node.
247680d8a24SJuraj Linkeš        When app_name is 'all', build all example apps.
248680d8a24SJuraj Linkeš        When app_name is any other string, tries to build that example app.
249680d8a24SJuraj Linkeš        Return the directory path of the built app. If building all apps, return
250680d8a24SJuraj Linkeš        the path to the examples directory (where all apps reside).
251680d8a24SJuraj Linkeš        The meson_dpdk_args are keyword arguments
252680d8a24SJuraj Linkeš        found in meson_option.txt in root DPDK directory. Do not use -D with them,
253680d8a24SJuraj Linkeš        for example: enable_kmods=True.
254680d8a24SJuraj Linkeš        """
255680d8a24SJuraj Linkeš        self.main_session.build_dpdk(
256680d8a24SJuraj Linkeš            self._env_vars,
257680d8a24SJuraj Linkeš            MesonArgs(examples=app_name, **meson_dpdk_args),  # type: ignore [arg-type]
258680d8a24SJuraj Linkeš            # ^^ https://github.com/python/mypy/issues/11583
259680d8a24SJuraj Linkeš            self._remote_dpdk_dir,
260680d8a24SJuraj Linkeš            self.remote_dpdk_build_dir,
261680d8a24SJuraj Linkeš            rebuild=True,
262680d8a24SJuraj Linkeš            timeout=self._app_compile_timeout,
263680d8a24SJuraj Linkeš        )
264680d8a24SJuraj Linkeš
265680d8a24SJuraj Linkeš        if app_name == "all":
266680d8a24SJuraj Linkeš            return self.main_session.join_remote_path(
267680d8a24SJuraj Linkeš                self.remote_dpdk_build_dir, "examples"
268680d8a24SJuraj Linkeš            )
269680d8a24SJuraj Linkeš        return self.main_session.join_remote_path(
270680d8a24SJuraj Linkeš            self.remote_dpdk_build_dir, "examples", f"dpdk-{app_name}"
271680d8a24SJuraj Linkeš        )
272c020b7ceSJuraj Linkeš
273c020b7ceSJuraj Linkeš    def kill_cleanup_dpdk_apps(self) -> None:
274c020b7ceSJuraj Linkeš        """
275c020b7ceSJuraj Linkeš        Kill all dpdk applications on the SUT. Cleanup hugepages.
276c020b7ceSJuraj Linkeš        """
277c020b7ceSJuraj Linkeš        if self._dpdk_kill_session and self._dpdk_kill_session.is_alive():
278c020b7ceSJuraj Linkeš            # we can use the session if it exists and responds
279c020b7ceSJuraj Linkeš            self._dpdk_kill_session.kill_cleanup_dpdk_apps(self._dpdk_prefix_list)
280c020b7ceSJuraj Linkeš        else:
281c020b7ceSJuraj Linkeš            # otherwise, we need to (re)create it
282c020b7ceSJuraj Linkeš            self._dpdk_kill_session = self.create_session("dpdk_kill")
283c020b7ceSJuraj Linkeš        self._dpdk_prefix_list = []
284c020b7ceSJuraj Linkeš
285c020b7ceSJuraj Linkeš    def create_eal_parameters(
286c020b7ceSJuraj Linkeš        self,
287c020b7ceSJuraj Linkeš        lcore_filter_specifier: LogicalCoreCount | LogicalCoreList = LogicalCoreCount(),
288c020b7ceSJuraj Linkeš        ascending_cores: bool = True,
289c020b7ceSJuraj Linkeš        prefix: str = "dpdk",
290c020b7ceSJuraj Linkeš        append_prefix_timestamp: bool = True,
291c020b7ceSJuraj Linkeš        no_pci: bool = False,
292c020b7ceSJuraj Linkeš        vdevs: list[VirtualDevice] = None,
293c020b7ceSJuraj Linkeš        other_eal_param: str = "",
294c020b7ceSJuraj Linkeš    ) -> "EalParameters":
295c020b7ceSJuraj Linkeš        """
296c020b7ceSJuraj Linkeš        Generate eal parameters character string;
297c020b7ceSJuraj Linkeš        :param lcore_filter_specifier: a number of lcores/cores/sockets to use
298c020b7ceSJuraj Linkeš                        or a list of lcore ids to use.
299c020b7ceSJuraj Linkeš                        The default will select one lcore for each of two cores
300c020b7ceSJuraj Linkeš                        on one socket, in ascending order of core ids.
301c020b7ceSJuraj Linkeš        :param ascending_cores: True, use cores with the lowest numerical id first
302c020b7ceSJuraj Linkeš                        and continue in ascending order. If False, start with the
303c020b7ceSJuraj Linkeš                        highest id and continue in descending order. This ordering
304c020b7ceSJuraj Linkeš                        affects which sockets to consider first as well.
305c020b7ceSJuraj Linkeš        :param prefix: set file prefix string, eg:
306c020b7ceSJuraj Linkeš                        prefix='vf'
307c020b7ceSJuraj Linkeš        :param append_prefix_timestamp: if True, will append a timestamp to
308c020b7ceSJuraj Linkeš                        DPDK file prefix.
309c020b7ceSJuraj Linkeš        :param no_pci: switch of disable PCI bus eg:
310c020b7ceSJuraj Linkeš                        no_pci=True
311c020b7ceSJuraj Linkeš        :param vdevs: virtual device list, eg:
312c020b7ceSJuraj Linkeš                        vdevs=[
313c020b7ceSJuraj Linkeš                            VirtualDevice('net_ring0'),
314c020b7ceSJuraj Linkeš                            VirtualDevice('net_ring1')
315c020b7ceSJuraj Linkeš                        ]
316c020b7ceSJuraj Linkeš        :param other_eal_param: user defined DPDK eal parameters, eg:
317c020b7ceSJuraj Linkeš                        other_eal_param='--single-file-segments'
318c020b7ceSJuraj Linkeš        :return: eal param string, eg:
319c020b7ceSJuraj Linkeš                '-c 0xf -a 0000:88:00.0 --file-prefix=dpdk_1112_20190809143420';
320c020b7ceSJuraj Linkeš        """
321c020b7ceSJuraj Linkeš
322c020b7ceSJuraj Linkeš        lcore_list = LogicalCoreList(
323c020b7ceSJuraj Linkeš            self.filter_lcores(lcore_filter_specifier, ascending_cores)
324c020b7ceSJuraj Linkeš        )
325c020b7ceSJuraj Linkeš
326c020b7ceSJuraj Linkeš        if append_prefix_timestamp:
327c020b7ceSJuraj Linkeš            prefix = f"{prefix}_{self._dpdk_timestamp}"
328c020b7ceSJuraj Linkeš        prefix = self.main_session.get_dpdk_file_prefix(prefix)
329c020b7ceSJuraj Linkeš        if prefix:
330c020b7ceSJuraj Linkeš            self._dpdk_prefix_list.append(prefix)
331c020b7ceSJuraj Linkeš
332c020b7ceSJuraj Linkeš        if vdevs is None:
333c020b7ceSJuraj Linkeš            vdevs = []
334c020b7ceSJuraj Linkeš
335c020b7ceSJuraj Linkeš        return EalParameters(
336c020b7ceSJuraj Linkeš            lcore_list=lcore_list,
337c020b7ceSJuraj Linkeš            memory_channels=self.config.memory_channels,
338c020b7ceSJuraj Linkeš            prefix=prefix,
339c020b7ceSJuraj Linkeš            no_pci=no_pci,
340c020b7ceSJuraj Linkeš            vdevs=vdevs,
341c020b7ceSJuraj Linkeš            other_eal_param=other_eal_param,
342c020b7ceSJuraj Linkeš        )
343c020b7ceSJuraj Linkeš
344444833c0SJuraj Linkeš    def run_dpdk_app(
345444833c0SJuraj Linkeš        self, app_path: PurePath, eal_args: "EalParameters", timeout: float = 30
346444833c0SJuraj Linkeš    ) -> CommandResult:
347444833c0SJuraj Linkeš        """
348444833c0SJuraj Linkeš        Run DPDK application on the remote node.
349444833c0SJuraj Linkeš        """
350444833c0SJuraj Linkeš        return self.main_session.send_command(
351b8bdc4c5SJuraj Linkeš            f"{app_path} {eal_args}", timeout, privileged=True, verify=True
352444833c0SJuraj Linkeš        )
353444833c0SJuraj Linkeš
35488489c05SJeremy Spewock    def create_interactive_shell(
355c020b7ceSJuraj Linkeš        self,
35688489c05SJeremy Spewock        shell_cls: Type[InteractiveShellType],
35788489c05SJeremy Spewock        timeout: float = SETTINGS.timeout,
35888489c05SJeremy Spewock        privileged: bool = False,
35988489c05SJeremy Spewock        eal_parameters: EalParameters | str | None = None,
36088489c05SJeremy Spewock    ) -> InteractiveShellType:
36188489c05SJeremy Spewock        """Factory method for creating a handler for an interactive session.
362c020b7ceSJuraj Linkeš
36388489c05SJeremy Spewock        Instantiate shell_cls according to the remote OS specifics.
36488489c05SJeremy Spewock
36588489c05SJeremy Spewock        Args:
36688489c05SJeremy Spewock            shell_cls: The class of the shell.
36788489c05SJeremy Spewock            timeout: Timeout for reading output from the SSH channel. If you are
36888489c05SJeremy Spewock                reading from the buffer and don't receive any data within the timeout
36988489c05SJeremy Spewock                it will throw an error.
37088489c05SJeremy Spewock            privileged: Whether to run the shell with administrative privileges.
37188489c05SJeremy Spewock            eal_parameters: List of EAL parameters to use to launch the app. If this
37288489c05SJeremy Spewock                isn't provided or an empty string is passed, it will default to calling
37388489c05SJeremy Spewock                create_eal_parameters().
37488489c05SJeremy Spewock        Returns:
37588489c05SJeremy Spewock            Instance of the desired interactive application.
37688489c05SJeremy Spewock        """
37788489c05SJeremy Spewock        if not eal_parameters:
37888489c05SJeremy Spewock            eal_parameters = self.create_eal_parameters()
37988489c05SJeremy Spewock
38088489c05SJeremy Spewock        # We need to append the build directory for DPDK apps
38188489c05SJeremy Spewock        if shell_cls.dpdk_app:
38288489c05SJeremy Spewock            shell_cls.path = self.main_session.join_remote_path(
38388489c05SJeremy Spewock                self.remote_dpdk_build_dir, shell_cls.path
38488489c05SJeremy Spewock            )
38588489c05SJeremy Spewock
38688489c05SJeremy Spewock        return super().create_interactive_shell(
38788489c05SJeremy Spewock            shell_cls, timeout, privileged, str(eal_parameters)
388c020b7ceSJuraj Linkeš        )
389