xref: /dpdk/dts/framework/testbed_model/posix_session.py (revision b935bdc3da26ab86ec775dfad3aa63a1a61f5667)
1840b1e01SJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause
2840b1e01SJuraj Linkeš# Copyright(c) 2023 PANTHEON.tech s.r.o.
3840b1e01SJuraj Linkeš# Copyright(c) 2023 University of New Hampshire
4840b1e01SJuraj Linkeš
56ef07151SJuraj Linkeš"""POSIX compliant OS translator.
66ef07151SJuraj Linkeš
76ef07151SJuraj LinkešTranslates OS-unaware calls into POSIX compliant calls/utilities. POSIX is a set of standards
86ef07151SJuraj Linkešfor portability between Unix operating systems which not all Linux distributions
96ef07151SJuraj Linkeš(or the tools most frequently bundled with said distributions) adhere to. Most of Linux
106ef07151SJuraj Linkešdistributions are mostly compliant though.
116ef07151SJuraj LinkešThis intermediate module implements the common parts of mostly POSIX compliant distributions.
126ef07151SJuraj Linkeš"""
136ef07151SJuraj Linkeš
14840b1e01SJuraj Linkešimport re
15840b1e01SJuraj Linkešfrom collections.abc import Iterable
16441c5fbfSTomáš Ďurovecfrom pathlib import Path, PurePath, PurePosixPath
17840b1e01SJuraj Linkeš
18c72ff85dSLuca Vizzarrofrom framework.config import Architecture
19840b1e01SJuraj Linkešfrom framework.exception import DPDKBuildError, RemoteCommandExecutionError
20840b1e01SJuraj Linkešfrom framework.settings import SETTINGS
2180158fd4STomáš Ďurovecfrom framework.utils import (
2280158fd4STomáš Ďurovec    MesonArgs,
2380158fd4STomáš Ďurovec    TarCompressionFormat,
2480158fd4STomáš Ďurovec    convert_to_list_of_string,
2580158fd4STomáš Ďurovec    create_tarball,
2680158fd4STomáš Ďurovec    extract_tarball,
2780158fd4STomáš Ďurovec)
28840b1e01SJuraj Linkeš
29c72ff85dSLuca Vizzarrofrom .os_session import OSSession, OSSessionInfo
30840b1e01SJuraj Linkeš
31840b1e01SJuraj Linkeš
32840b1e01SJuraj Linkešclass PosixSession(OSSession):
336ef07151SJuraj Linkeš    """An intermediary class implementing the POSIX standard."""
34840b1e01SJuraj Linkeš
35840b1e01SJuraj Linkeš    @staticmethod
36840b1e01SJuraj Linkeš    def combine_short_options(**opts: bool) -> str:
376ef07151SJuraj Linkeš        """Combine shell options into one argument.
386ef07151SJuraj Linkeš
396ef07151SJuraj Linkeš        These are options such as ``-x``, ``-v``, ``-f`` which are combined into ``-xvf``.
406ef07151SJuraj Linkeš
416ef07151SJuraj Linkeš        Args:
426ef07151SJuraj Linkeš            opts: The keys are option names (usually one letter) and the bool values indicate
436ef07151SJuraj Linkeš                whether to include the option in the resulting argument.
446ef07151SJuraj Linkeš
456ef07151SJuraj Linkeš        Returns:
466ef07151SJuraj Linkeš            The options combined into one argument.
476ef07151SJuraj Linkeš        """
48840b1e01SJuraj Linkeš        ret_opts = ""
49840b1e01SJuraj Linkeš        for opt, include in opts.items():
50840b1e01SJuraj Linkeš            if include:
51840b1e01SJuraj Linkeš                ret_opts = f"{ret_opts}{opt}"
52840b1e01SJuraj Linkeš
53840b1e01SJuraj Linkeš        if ret_opts:
54840b1e01SJuraj Linkeš            ret_opts = f" -{ret_opts}"
55840b1e01SJuraj Linkeš
56840b1e01SJuraj Linkeš        return ret_opts
57840b1e01SJuraj Linkeš
58840b1e01SJuraj Linkeš    def guess_dpdk_remote_dir(self, remote_dir: str | PurePath) -> PurePosixPath:
596ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.guess_dpdk_remote_dir`."""
60840b1e01SJuraj Linkeš        remote_guess = self.join_remote_path(remote_dir, "dpdk-*")
61840b1e01SJuraj Linkeš        result = self.send_command(f"ls -d {remote_guess} | tail -1")
62840b1e01SJuraj Linkeš        return PurePosixPath(result.stdout)
63840b1e01SJuraj Linkeš
64840b1e01SJuraj Linkeš    def get_remote_tmp_dir(self) -> PurePosixPath:
656ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.get_remote_tmp_dir`."""
66840b1e01SJuraj Linkeš        return PurePosixPath("/tmp")
67840b1e01SJuraj Linkeš
68840b1e01SJuraj Linkeš    def get_dpdk_build_env_vars(self, arch: Architecture) -> dict:
696ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.get_dpdk_build_env_vars`.
706ef07151SJuraj Linkeš
716ef07151SJuraj Linkeš        Supported architecture: ``i686``.
72840b1e01SJuraj Linkeš        """
73840b1e01SJuraj Linkeš        env_vars = {}
74840b1e01SJuraj Linkeš        if arch == Architecture.i686:
75840b1e01SJuraj Linkeš            # find the pkg-config path and store it in PKG_CONFIG_LIBDIR
76840b1e01SJuraj Linkeš            out = self.send_command("find /usr -type d -name pkgconfig")
77840b1e01SJuraj Linkeš            pkg_path = ""
78840b1e01SJuraj Linkeš            res_path = out.stdout.split("\r\n")
79840b1e01SJuraj Linkeš            for cur_path in res_path:
80840b1e01SJuraj Linkeš                if "i386" in cur_path:
81840b1e01SJuraj Linkeš                    pkg_path = cur_path
82840b1e01SJuraj Linkeš                    break
83840b1e01SJuraj Linkeš            assert pkg_path != "", "i386 pkg-config path not found"
84840b1e01SJuraj Linkeš
85840b1e01SJuraj Linkeš            env_vars["CFLAGS"] = "-m32"
86840b1e01SJuraj Linkeš            env_vars["PKG_CONFIG_LIBDIR"] = pkg_path
87840b1e01SJuraj Linkeš
88840b1e01SJuraj Linkeš        return env_vars
89840b1e01SJuraj Linkeš
90840b1e01SJuraj Linkeš    def join_remote_path(self, *args: str | PurePath) -> PurePosixPath:
916ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.join_remote_path`."""
92840b1e01SJuraj Linkeš        return PurePosixPath(*args)
93840b1e01SJuraj Linkeš
94f9957667STomáš Ďurovec    def remote_path_exists(self, remote_path: str | PurePath) -> bool:
95f9957667STomáš Ďurovec        """Overrides :meth:`~.os_session.OSSession.remote_path_exists`."""
96f9957667STomáš Ďurovec        result = self.send_command(f"test -e {remote_path}")
97f9957667STomáš Ďurovec        return not result.return_code
98f9957667STomáš Ďurovec
99441c5fbfSTomáš Ďurovec    def copy_from(self, source_file: str | PurePath, destination_dir: str | Path) -> None:
1006ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.copy_from`."""
101441c5fbfSTomáš Ďurovec        self.remote_session.copy_from(source_file, destination_dir)
102840b1e01SJuraj Linkeš
103441c5fbfSTomáš Ďurovec    def copy_to(self, source_file: str | Path, destination_dir: str | PurePath) -> None:
1046ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.copy_to`."""
105441c5fbfSTomáš Ďurovec        self.remote_session.copy_to(source_file, destination_dir)
106840b1e01SJuraj Linkeš
10780158fd4STomáš Ďurovec    def copy_dir_from(
10880158fd4STomáš Ďurovec        self,
10980158fd4STomáš Ďurovec        source_dir: str | PurePath,
11080158fd4STomáš Ďurovec        destination_dir: str | Path,
11180158fd4STomáš Ďurovec        compress_format: TarCompressionFormat = TarCompressionFormat.none,
11280158fd4STomáš Ďurovec        exclude: str | list[str] | None = None,
11380158fd4STomáš Ďurovec    ) -> None:
11480158fd4STomáš Ďurovec        """Overrides :meth:`~.os_session.OSSession.copy_dir_from`."""
11580158fd4STomáš Ďurovec        source_dir = PurePath(source_dir)
11680158fd4STomáš Ďurovec        remote_tarball_path = self.create_remote_tarball(source_dir, compress_format, exclude)
11780158fd4STomáš Ďurovec
11880158fd4STomáš Ďurovec        self.copy_from(remote_tarball_path, destination_dir)
11980158fd4STomáš Ďurovec        self.remove_remote_file(remote_tarball_path)
12080158fd4STomáš Ďurovec
12180158fd4STomáš Ďurovec        tarball_path = Path(destination_dir, f"{source_dir.name}.{compress_format.extension}")
12280158fd4STomáš Ďurovec        extract_tarball(tarball_path)
12380158fd4STomáš Ďurovec        tarball_path.unlink()
12480158fd4STomáš Ďurovec
12580158fd4STomáš Ďurovec    def copy_dir_to(
12680158fd4STomáš Ďurovec        self,
12780158fd4STomáš Ďurovec        source_dir: str | Path,
12880158fd4STomáš Ďurovec        destination_dir: str | PurePath,
12980158fd4STomáš Ďurovec        compress_format: TarCompressionFormat = TarCompressionFormat.none,
13080158fd4STomáš Ďurovec        exclude: str | list[str] | None = None,
13180158fd4STomáš Ďurovec    ) -> None:
13280158fd4STomáš Ďurovec        """Overrides :meth:`~.os_session.OSSession.copy_dir_to`."""
13380158fd4STomáš Ďurovec        source_dir = Path(source_dir)
13480158fd4STomáš Ďurovec        tarball_path = create_tarball(source_dir, compress_format, exclude=exclude)
13580158fd4STomáš Ďurovec        self.copy_to(tarball_path, destination_dir)
13680158fd4STomáš Ďurovec        tarball_path.unlink()
13780158fd4STomáš Ďurovec
13880158fd4STomáš Ďurovec        remote_tar_path = self.join_remote_path(
13980158fd4STomáš Ďurovec            destination_dir, f"{source_dir.name}.{compress_format.extension}"
14080158fd4STomáš Ďurovec        )
14180158fd4STomáš Ďurovec        self.extract_remote_tarball(remote_tar_path)
14280158fd4STomáš Ďurovec        self.remove_remote_file(remote_tar_path)
14380158fd4STomáš Ďurovec
14480158fd4STomáš Ďurovec    def remove_remote_file(self, remote_file_path: str | PurePath, force: bool = True) -> None:
14580158fd4STomáš Ďurovec        """Overrides :meth:`~.os_session.OSSession.remove_remote_dir`."""
14680158fd4STomáš Ďurovec        opts = PosixSession.combine_short_options(f=force)
14780158fd4STomáš Ďurovec        self.send_command(f"rm{opts} {remote_file_path}")
14880158fd4STomáš Ďurovec
149840b1e01SJuraj Linkeš    def remove_remote_dir(
150840b1e01SJuraj Linkeš        self,
151840b1e01SJuraj Linkeš        remote_dir_path: str | PurePath,
152840b1e01SJuraj Linkeš        recursive: bool = True,
153840b1e01SJuraj Linkeš        force: bool = True,
154840b1e01SJuraj Linkeš    ) -> None:
1556ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.remove_remote_dir`."""
156840b1e01SJuraj Linkeš        opts = PosixSession.combine_short_options(r=recursive, f=force)
157840b1e01SJuraj Linkeš        self.send_command(f"rm{opts} {remote_dir_path}")
158840b1e01SJuraj Linkeš
15980158fd4STomáš Ďurovec    def create_remote_tarball(
160840b1e01SJuraj Linkeš        self,
16180158fd4STomáš Ďurovec        remote_dir_path: str | PurePath,
16280158fd4STomáš Ďurovec        compress_format: TarCompressionFormat = TarCompressionFormat.none,
16380158fd4STomáš Ďurovec        exclude: str | list[str] | None = None,
16480158fd4STomáš Ďurovec    ) -> PurePosixPath:
16580158fd4STomáš Ďurovec        """Overrides :meth:`~.os_session.OSSession.create_remote_tarball`."""
16680158fd4STomáš Ďurovec
16780158fd4STomáš Ďurovec        def generate_tar_exclude_args(exclude_patterns) -> str:
16880158fd4STomáš Ďurovec            """Generate args to exclude patterns when creating a tarball.
16980158fd4STomáš Ďurovec
17080158fd4STomáš Ďurovec            Args:
17180158fd4STomáš Ďurovec                exclude_patterns: Patterns for files or directories to exclude from the tarball.
17280158fd4STomáš Ďurovec                    These patterns are used with `tar`'s `--exclude` option.
17380158fd4STomáš Ďurovec
17480158fd4STomáš Ďurovec            Returns:
17580158fd4STomáš Ďurovec                The generated string args to exclude the specified patterns.
17680158fd4STomáš Ďurovec            """
17780158fd4STomáš Ďurovec            if exclude_patterns:
17880158fd4STomáš Ďurovec                exclude_patterns = convert_to_list_of_string(exclude_patterns)
17980158fd4STomáš Ďurovec                return "".join([f" --exclude={pattern}" for pattern in exclude_patterns])
18080158fd4STomáš Ďurovec            return ""
18180158fd4STomáš Ďurovec
18280158fd4STomáš Ďurovec        posix_remote_dir_path = PurePosixPath(remote_dir_path)
18380158fd4STomáš Ďurovec        target_tarball_path = PurePosixPath(f"{remote_dir_path}.{compress_format.extension}")
18480158fd4STomáš Ďurovec
18580158fd4STomáš Ďurovec        self.send_command(
18680158fd4STomáš Ďurovec            f"tar caf {target_tarball_path}{generate_tar_exclude_args(exclude)} "
18780158fd4STomáš Ďurovec            f"-C {posix_remote_dir_path.parent} {posix_remote_dir_path.name}",
18880158fd4STomáš Ďurovec            60,
18980158fd4STomáš Ďurovec        )
19080158fd4STomáš Ďurovec
19180158fd4STomáš Ďurovec        return target_tarball_path
19280158fd4STomáš Ďurovec
19380158fd4STomáš Ďurovec    def extract_remote_tarball(
19480158fd4STomáš Ďurovec        self, remote_tarball_path: str | PurePath, expected_dir: str | PurePath | None = None
195840b1e01SJuraj Linkeš    ) -> None:
1966ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.extract_remote_tarball`."""
197840b1e01SJuraj Linkeš        self.send_command(
198840b1e01SJuraj Linkeš            f"tar xfm {remote_tarball_path} -C {PurePosixPath(remote_tarball_path).parent}",
199840b1e01SJuraj Linkeš            60,
200840b1e01SJuraj Linkeš        )
201840b1e01SJuraj Linkeš        if expected_dir:
202840b1e01SJuraj Linkeš            self.send_command(f"ls {expected_dir}", verify=True)
203840b1e01SJuraj Linkeš
204*b935bdc3SLuca Vizzarro    def is_remote_dir(self, remote_path: PurePath) -> bool:
205f9957667STomáš Ďurovec        """Overrides :meth:`~.os_session.OSSession.is_remote_dir`."""
206f9957667STomáš Ďurovec        result = self.send_command(f"test -d {remote_path}")
207f9957667STomáš Ďurovec        return not result.return_code
208f9957667STomáš Ďurovec
209*b935bdc3SLuca Vizzarro    def is_remote_tarfile(self, remote_tarball_path: PurePath) -> bool:
210f9957667STomáš Ďurovec        """Overrides :meth:`~.os_session.OSSession.is_remote_tarfile`."""
211f9957667STomáš Ďurovec        result = self.send_command(f"tar -tvf {remote_tarball_path}")
212f9957667STomáš Ďurovec        return not result.return_code
213f9957667STomáš Ďurovec
214f9957667STomáš Ďurovec    def get_tarball_top_dir(
215f9957667STomáš Ďurovec        self, remote_tarball_path: str | PurePath
216f9957667STomáš Ďurovec    ) -> str | PurePosixPath | None:
217f9957667STomáš Ďurovec        """Overrides :meth:`~.os_session.OSSession.get_tarball_top_dir`."""
218f9957667STomáš Ďurovec        members = self.send_command(f"tar tf {remote_tarball_path}").stdout.split()
219f9957667STomáš Ďurovec
220f9957667STomáš Ďurovec        top_dirs = []
221f9957667STomáš Ďurovec        for member in members:
222f9957667STomáš Ďurovec            parts_of_member = PurePosixPath(member).parts
223f9957667STomáš Ďurovec            if parts_of_member:
224f9957667STomáš Ďurovec                top_dirs.append(parts_of_member[0])
225f9957667STomáš Ďurovec
226f9957667STomáš Ďurovec        if len(set(top_dirs)) == 1:
227f9957667STomáš Ďurovec            return top_dirs[0]
228f9957667STomáš Ďurovec        return None
229f9957667STomáš Ďurovec
230840b1e01SJuraj Linkeš    def build_dpdk(
231840b1e01SJuraj Linkeš        self,
232840b1e01SJuraj Linkeš        env_vars: dict,
233840b1e01SJuraj Linkeš        meson_args: MesonArgs,
234840b1e01SJuraj Linkeš        remote_dpdk_dir: str | PurePath,
235840b1e01SJuraj Linkeš        remote_dpdk_build_dir: str | PurePath,
236840b1e01SJuraj Linkeš        rebuild: bool = False,
237840b1e01SJuraj Linkeš        timeout: float = SETTINGS.compile_timeout,
238840b1e01SJuraj Linkeš    ) -> None:
2396ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.build_dpdk`."""
240840b1e01SJuraj Linkeš        try:
241840b1e01SJuraj Linkeš            if rebuild:
242840b1e01SJuraj Linkeš                # reconfigure, then build
243840b1e01SJuraj Linkeš                self._logger.info("Reconfiguring DPDK build.")
244840b1e01SJuraj Linkeš                self.send_command(
245840b1e01SJuraj Linkeš                    f"meson configure {meson_args} {remote_dpdk_build_dir}",
246840b1e01SJuraj Linkeš                    timeout,
247840b1e01SJuraj Linkeš                    verify=True,
248840b1e01SJuraj Linkeš                    env=env_vars,
249840b1e01SJuraj Linkeš                )
250840b1e01SJuraj Linkeš            else:
251840b1e01SJuraj Linkeš                # fresh build - remove target dir first, then build from scratch
252840b1e01SJuraj Linkeš                self._logger.info("Configuring DPDK build from scratch.")
253840b1e01SJuraj Linkeš                self.remove_remote_dir(remote_dpdk_build_dir)
254840b1e01SJuraj Linkeš                self.send_command(
255840b1e01SJuraj Linkeš                    f"meson setup {meson_args} {remote_dpdk_dir} {remote_dpdk_build_dir}",
256840b1e01SJuraj Linkeš                    timeout,
257840b1e01SJuraj Linkeš                    verify=True,
258840b1e01SJuraj Linkeš                    env=env_vars,
259840b1e01SJuraj Linkeš                )
260840b1e01SJuraj Linkeš
261840b1e01SJuraj Linkeš            self._logger.info("Building DPDK.")
262840b1e01SJuraj Linkeš            self.send_command(
263840b1e01SJuraj Linkeš                f"ninja -C {remote_dpdk_build_dir}", timeout, verify=True, env=env_vars
264840b1e01SJuraj Linkeš            )
265840b1e01SJuraj Linkeš        except RemoteCommandExecutionError as e:
266840b1e01SJuraj Linkeš            raise DPDKBuildError(f"DPDK build failed when doing '{e.command}'.")
267840b1e01SJuraj Linkeš
268840b1e01SJuraj Linkeš    def get_dpdk_version(self, build_dir: str | PurePath) -> str:
2696ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.get_dpdk_version`."""
270840b1e01SJuraj Linkeš        out = self.send_command(f"cat {self.join_remote_path(build_dir, 'VERSION')}", verify=True)
271840b1e01SJuraj Linkeš        return out.stdout
272840b1e01SJuraj Linkeš
273840b1e01SJuraj Linkeš    def kill_cleanup_dpdk_apps(self, dpdk_prefix_list: Iterable[str]) -> None:
2746ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.kill_cleanup_dpdk_apps`."""
275840b1e01SJuraj Linkeš        self._logger.info("Cleaning up DPDK apps.")
276840b1e01SJuraj Linkeš        dpdk_runtime_dirs = self._get_dpdk_runtime_dirs(dpdk_prefix_list)
277840b1e01SJuraj Linkeš        if dpdk_runtime_dirs:
278840b1e01SJuraj Linkeš            # kill and cleanup only if DPDK is running
279840b1e01SJuraj Linkeš            dpdk_pids = self._get_dpdk_pids(dpdk_runtime_dirs)
280840b1e01SJuraj Linkeš            for dpdk_pid in dpdk_pids:
281840b1e01SJuraj Linkeš                self.send_command(f"kill -9 {dpdk_pid}", 20)
282840b1e01SJuraj Linkeš            self._check_dpdk_hugepages(dpdk_runtime_dirs)
283840b1e01SJuraj Linkeš            self._remove_dpdk_runtime_dirs(dpdk_runtime_dirs)
284840b1e01SJuraj Linkeš
285840b1e01SJuraj Linkeš    def _get_dpdk_runtime_dirs(self, dpdk_prefix_list: Iterable[str]) -> list[PurePosixPath]:
2866ef07151SJuraj Linkeš        """Find runtime directories DPDK apps are currently using.
2876ef07151SJuraj Linkeš
2886ef07151SJuraj Linkeš        Args:
2896ef07151SJuraj Linkeš              dpdk_prefix_list: The prefixes DPDK apps were started with.
2906ef07151SJuraj Linkeš
2916ef07151SJuraj Linkeš        Returns:
2926ef07151SJuraj Linkeš            The paths of DPDK apps' runtime dirs.
2936ef07151SJuraj Linkeš        """
294840b1e01SJuraj Linkeš        prefix = PurePosixPath("/var", "run", "dpdk")
295840b1e01SJuraj Linkeš        if not dpdk_prefix_list:
296840b1e01SJuraj Linkeš            remote_prefixes = self._list_remote_dirs(prefix)
297840b1e01SJuraj Linkeš            if not remote_prefixes:
298840b1e01SJuraj Linkeš                dpdk_prefix_list = []
299840b1e01SJuraj Linkeš            else:
300840b1e01SJuraj Linkeš                dpdk_prefix_list = remote_prefixes
301840b1e01SJuraj Linkeš
302840b1e01SJuraj Linkeš        return [PurePosixPath(prefix, dpdk_prefix) for dpdk_prefix in dpdk_prefix_list]
303840b1e01SJuraj Linkeš
304840b1e01SJuraj Linkeš    def _list_remote_dirs(self, remote_path: str | PurePath) -> list[str] | None:
3056ef07151SJuraj Linkeš        """Contents of remote_path.
3066ef07151SJuraj Linkeš
3076ef07151SJuraj Linkeš        Args:
3086ef07151SJuraj Linkeš            remote_path: List the contents of this path.
3096ef07151SJuraj Linkeš
3106ef07151SJuraj Linkeš        Returns:
3116ef07151SJuraj Linkeš            The contents of remote_path. If remote_path doesn't exist, return None.
312840b1e01SJuraj Linkeš        """
313840b1e01SJuraj Linkeš        out = self.send_command(f"ls -l {remote_path} | awk '/^d/ {{print $NF}}'").stdout
314840b1e01SJuraj Linkeš        if "No such file or directory" in out:
315840b1e01SJuraj Linkeš            return None
316840b1e01SJuraj Linkeš        else:
317840b1e01SJuraj Linkeš            return out.splitlines()
318840b1e01SJuraj Linkeš
319840b1e01SJuraj Linkeš    def _get_dpdk_pids(self, dpdk_runtime_dirs: Iterable[str | PurePath]) -> list[int]:
3206ef07151SJuraj Linkeš        """Find PIDs of running DPDK apps.
3216ef07151SJuraj Linkeš
3226ef07151SJuraj Linkeš        Look at each "config" file found in dpdk_runtime_dirs and find the PIDs of processes
3236ef07151SJuraj Linkeš        that opened those file.
3246ef07151SJuraj Linkeš
3256ef07151SJuraj Linkeš        Args:
3266ef07151SJuraj Linkeš            dpdk_runtime_dirs: The paths of DPDK apps' runtime dirs.
3276ef07151SJuraj Linkeš
3286ef07151SJuraj Linkeš        Returns:
3296ef07151SJuraj Linkeš            The PIDs of running DPDK apps.
3306ef07151SJuraj Linkeš        """
331840b1e01SJuraj Linkeš        pids = []
332840b1e01SJuraj Linkeš        pid_regex = r"p(\d+)"
333840b1e01SJuraj Linkeš        for dpdk_runtime_dir in dpdk_runtime_dirs:
334840b1e01SJuraj Linkeš            dpdk_config_file = PurePosixPath(dpdk_runtime_dir, "config")
335f9957667STomáš Ďurovec            if self.remote_path_exists(dpdk_config_file):
336840b1e01SJuraj Linkeš                out = self.send_command(f"lsof -Fp {dpdk_config_file}").stdout
337840b1e01SJuraj Linkeš                if out and "No such file or directory" not in out:
338840b1e01SJuraj Linkeš                    for out_line in out.splitlines():
339840b1e01SJuraj Linkeš                        match = re.match(pid_regex, out_line)
340840b1e01SJuraj Linkeš                        if match:
341840b1e01SJuraj Linkeš                            pids.append(int(match.group(1)))
342840b1e01SJuraj Linkeš        return pids
343840b1e01SJuraj Linkeš
344840b1e01SJuraj Linkeš    def _check_dpdk_hugepages(self, dpdk_runtime_dirs: Iterable[str | PurePath]) -> None:
3456ef07151SJuraj Linkeš        """Check there aren't any leftover hugepages.
3466ef07151SJuraj Linkeš
3476ef07151SJuraj Linkeš        If any hugepages are found, emit a warning. The hugepages are investigated in the
3486ef07151SJuraj Linkeš        "hugepage_info" file of dpdk_runtime_dirs.
3496ef07151SJuraj Linkeš
3506ef07151SJuraj Linkeš        Args:
3516ef07151SJuraj Linkeš            dpdk_runtime_dirs: The paths of DPDK apps' runtime dirs.
3526ef07151SJuraj Linkeš        """
353840b1e01SJuraj Linkeš        for dpdk_runtime_dir in dpdk_runtime_dirs:
354840b1e01SJuraj Linkeš            hugepage_info = PurePosixPath(dpdk_runtime_dir, "hugepage_info")
355f9957667STomáš Ďurovec            if self.remote_path_exists(hugepage_info):
356840b1e01SJuraj Linkeš                out = self.send_command(f"lsof -Fp {hugepage_info}").stdout
357840b1e01SJuraj Linkeš                if out and "No such file or directory" not in out:
358840b1e01SJuraj Linkeš                    self._logger.warning("Some DPDK processes did not free hugepages.")
359840b1e01SJuraj Linkeš                    self._logger.warning("*******************************************")
360840b1e01SJuraj Linkeš                    self._logger.warning(out)
361840b1e01SJuraj Linkeš                    self._logger.warning("*******************************************")
362840b1e01SJuraj Linkeš
363840b1e01SJuraj Linkeš    def _remove_dpdk_runtime_dirs(self, dpdk_runtime_dirs: Iterable[str | PurePath]) -> None:
364840b1e01SJuraj Linkeš        for dpdk_runtime_dir in dpdk_runtime_dirs:
365840b1e01SJuraj Linkeš            self.remove_remote_dir(dpdk_runtime_dir)
366840b1e01SJuraj Linkeš
367840b1e01SJuraj Linkeš    def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
3686ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.get_dpdk_file_prefix`."""
369840b1e01SJuraj Linkeš        return ""
370840b1e01SJuraj Linkeš
371840b1e01SJuraj Linkeš    def get_compiler_version(self, compiler_name: str) -> str:
3726ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.get_compiler_version`."""
373840b1e01SJuraj Linkeš        match compiler_name:
374840b1e01SJuraj Linkeš            case "gcc":
375840b1e01SJuraj Linkeš                return self.send_command(
376840b1e01SJuraj Linkeš                    f"{compiler_name} --version", SETTINGS.timeout
377840b1e01SJuraj Linkeš                ).stdout.split("\n")[0]
378840b1e01SJuraj Linkeš            case "clang":
379840b1e01SJuraj Linkeš                return self.send_command(
380840b1e01SJuraj Linkeš                    f"{compiler_name} --version", SETTINGS.timeout
381840b1e01SJuraj Linkeš                ).stdout.split("\n")[0]
382840b1e01SJuraj Linkeš            case "msvc":
383840b1e01SJuraj Linkeš                return self.send_command("cl", SETTINGS.timeout).stdout
384840b1e01SJuraj Linkeš            case "icc":
385840b1e01SJuraj Linkeš                return self.send_command(f"{compiler_name} -V", SETTINGS.timeout).stdout
386840b1e01SJuraj Linkeš            case _:
387840b1e01SJuraj Linkeš                raise ValueError(f"Unknown compiler {compiler_name}")
388840b1e01SJuraj Linkeš
389c72ff85dSLuca Vizzarro    def get_node_info(self) -> OSSessionInfo:
3906ef07151SJuraj Linkeš        """Overrides :meth:`~.os_session.OSSession.get_node_info`."""
391840b1e01SJuraj Linkeš        os_release_info = self.send_command(
392840b1e01SJuraj Linkeš            "awk -F= '$1 ~ /^NAME$|^VERSION$/ {print $2}' /etc/os-release",
393840b1e01SJuraj Linkeš            SETTINGS.timeout,
394840b1e01SJuraj Linkeš        ).stdout.split("\n")
395840b1e01SJuraj Linkeš        kernel_version = self.send_command("uname -r", SETTINGS.timeout).stdout
396c72ff85dSLuca Vizzarro        return OSSessionInfo(os_release_info[0].strip(), os_release_info[1].strip(), kernel_version)
397