xref: /dpdk/dts/framework/testbed_model/os_session.py (revision e3ab9dd5cd5d5e7cb117507ba9580dae9706c1f5)
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
4fd8cd8eeSLuca Vizzarro# Copyright(c) 2024 Arm Limited
5840b1e01SJuraj Linkeš
66ef07151SJuraj Linkeš"""OS-aware remote session.
76ef07151SJuraj Linkeš
86ef07151SJuraj LinkešDPDK supports multiple different operating systems, meaning it can run on these different operating
96ef07151SJuraj Linkešsystems. This module defines the common API that OS-unaware layers use and translates the API into
106ef07151SJuraj LinkešOS-aware calls/utility usage.
116ef07151SJuraj Linkeš
126ef07151SJuraj LinkešNote:
136ef07151SJuraj Linkeš    Running commands with administrative privileges requires OS awareness. This is the only layer
146ef07151SJuraj Linkeš    that's aware of OS differences, so this is where non-privileged command get converted
156ef07151SJuraj Linkeš    to privileged commands.
166ef07151SJuraj Linkeš
176ef07151SJuraj LinkešExample:
186ef07151SJuraj Linkeš    A user wishes to remove a directory on a remote :class:`~.sut_node.SutNode`.
196ef07151SJuraj Linkeš    The :class:`~.sut_node.SutNode` object isn't aware what OS the node is running - it delegates
206ef07151SJuraj Linkeš    the OS translation logic to :attr:`~.node.Node.main_session`. The SUT node calls
216ef07151SJuraj Linkeš    :meth:`~OSSession.remove_remote_dir` with a generic, OS-unaware path and
226ef07151SJuraj Linkeš    the :attr:`~.node.Node.main_session` translates that to ``rm -rf`` if the node's OS is Linux
236ef07151SJuraj Linkeš    and other commands for other OSs. It also translates the path to match the underlying OS.
246ef07151SJuraj Linkeš"""
25840b1e01SJuraj Linkešfrom abc import ABC, abstractmethod
26840b1e01SJuraj Linkešfrom collections.abc import Iterable
27c72ff85dSLuca Vizzarrofrom dataclasses import dataclass
2880158fd4STomáš Ďurovecfrom pathlib import Path, PurePath, PurePosixPath
29840b1e01SJuraj Linkeš
30c72ff85dSLuca Vizzarrofrom framework.config import Architecture, NodeConfiguration
3104f5a5a6SJuraj Linkešfrom framework.logger import DTSLogger
32840b1e01SJuraj Linkešfrom framework.remote_session import (
33840b1e01SJuraj Linkeš    InteractiveRemoteSession,
34840b1e01SJuraj Linkeš    RemoteSession,
35840b1e01SJuraj Linkeš    create_interactive_session,
36840b1e01SJuraj Linkeš    create_remote_session,
37840b1e01SJuraj Linkeš)
382b2f5a8aSLuca Vizzarrofrom framework.remote_session.remote_session import CommandResult
39840b1e01SJuraj Linkešfrom framework.settings import SETTINGS
4080158fd4STomáš Ďurovecfrom framework.utils import MesonArgs, TarCompressionFormat
41840b1e01SJuraj Linkeš
42840b1e01SJuraj Linkešfrom .cpu import LogicalCore
43840b1e01SJuraj Linkešfrom .port import Port
44840b1e01SJuraj Linkeš
45840b1e01SJuraj Linkeš
46c72ff85dSLuca Vizzarro@dataclass(slots=True, frozen=True)
47c72ff85dSLuca Vizzarroclass OSSessionInfo:
48c72ff85dSLuca Vizzarro    """Supplemental OS session information.
49c72ff85dSLuca Vizzarro
50c72ff85dSLuca Vizzarro    Attributes:
51c72ff85dSLuca Vizzarro        os_name: The name of the running operating system of
52c72ff85dSLuca Vizzarro            the :class:`~framework.testbed_model.node.Node`.
53c72ff85dSLuca Vizzarro        os_version: The version of the running operating system of
54c72ff85dSLuca Vizzarro            the :class:`~framework.testbed_model.node.Node`.
55c72ff85dSLuca Vizzarro        kernel_version: The kernel version of the running operating system of
56c72ff85dSLuca Vizzarro            the :class:`~framework.testbed_model.node.Node`.
57c72ff85dSLuca Vizzarro    """
58c72ff85dSLuca Vizzarro
59c72ff85dSLuca Vizzarro    os_name: str
60c72ff85dSLuca Vizzarro    os_version: str
61c72ff85dSLuca Vizzarro    kernel_version: str
62c72ff85dSLuca Vizzarro
63c72ff85dSLuca Vizzarro
64840b1e01SJuraj Linkešclass OSSession(ABC):
656ef07151SJuraj Linkeš    """OS-unaware to OS-aware translation API definition.
666ef07151SJuraj Linkeš
676ef07151SJuraj Linkeš    The OSSession classes create a remote session to a DTS node and implement OS specific
68840b1e01SJuraj Linkeš    behavior. There a few control methods implemented by the base class, the rest need
696ef07151SJuraj Linkeš    to be implemented by subclasses.
706ef07151SJuraj Linkeš
716ef07151SJuraj Linkeš    Attributes:
726ef07151SJuraj Linkeš        name: The name of the session.
736ef07151SJuraj Linkeš        remote_session: The remote session maintaining the connection to the node.
746ef07151SJuraj Linkeš        interactive_session: The interactive remote session maintaining the connection to the node.
75840b1e01SJuraj Linkeš    """
76840b1e01SJuraj Linkeš
77840b1e01SJuraj Linkeš    _config: NodeConfiguration
78840b1e01SJuraj Linkeš    name: str
7904f5a5a6SJuraj Linkeš    _logger: DTSLogger
80840b1e01SJuraj Linkeš    remote_session: RemoteSession
81840b1e01SJuraj Linkeš    interactive_session: InteractiveRemoteSession
82e5307b25SNicholas Pratte    hugepage_size: int
83840b1e01SJuraj Linkeš
84840b1e01SJuraj Linkeš    def __init__(
85840b1e01SJuraj Linkeš        self,
86840b1e01SJuraj Linkeš        node_config: NodeConfiguration,
87840b1e01SJuraj Linkeš        name: str,
8804f5a5a6SJuraj Linkeš        logger: DTSLogger,
89840b1e01SJuraj Linkeš    ):
906ef07151SJuraj Linkeš        """Initialize the OS-aware session.
916ef07151SJuraj Linkeš
926ef07151SJuraj Linkeš        Connect to the node right away and also create an interactive remote session.
936ef07151SJuraj Linkeš
946ef07151SJuraj Linkeš        Args:
956ef07151SJuraj Linkeš            node_config: The test run configuration of the node to connect to.
966ef07151SJuraj Linkeš            name: The name of the session.
976ef07151SJuraj Linkeš            logger: The logger instance this session will use.
986ef07151SJuraj Linkeš        """
99e5307b25SNicholas Pratte        self.hugepage_size = 2048
100840b1e01SJuraj Linkeš        self._config = node_config
101840b1e01SJuraj Linkeš        self.name = name
102840b1e01SJuraj Linkeš        self._logger = logger
103840b1e01SJuraj Linkeš        self.remote_session = create_remote_session(node_config, name, logger)
104840b1e01SJuraj Linkeš        self.interactive_session = create_interactive_session(node_config, logger)
105840b1e01SJuraj Linkeš
106840b1e01SJuraj Linkeš    def is_alive(self) -> bool:
1076ef07151SJuraj Linkeš        """Check whether the underlying remote session is still responding."""
108840b1e01SJuraj Linkeš        return self.remote_session.is_alive()
109840b1e01SJuraj Linkeš
110840b1e01SJuraj Linkeš    def send_command(
111840b1e01SJuraj Linkeš        self,
112840b1e01SJuraj Linkeš        command: str,
113840b1e01SJuraj Linkeš        timeout: float = SETTINGS.timeout,
114840b1e01SJuraj Linkeš        privileged: bool = False,
115840b1e01SJuraj Linkeš        verify: bool = False,
116840b1e01SJuraj Linkeš        env: dict | None = None,
117840b1e01SJuraj Linkeš    ) -> CommandResult:
1186ef07151SJuraj Linkeš        """An all-purpose API for OS-agnostic commands.
1196ef07151SJuraj Linkeš
1206ef07151SJuraj Linkeš        This can be used for an execution of a portable command that's executed the same way
1216ef07151SJuraj Linkeš        on all operating systems, such as Python.
1226ef07151SJuraj Linkeš
1236ef07151SJuraj Linkeš        The :option:`--timeout` command line argument and the :envvar:`DTS_TIMEOUT`
1246ef07151SJuraj Linkeš        environment variable configure the timeout of command execution.
1256ef07151SJuraj Linkeš
1266ef07151SJuraj Linkeš        Args:
1276ef07151SJuraj Linkeš            command: The command to execute.
1286ef07151SJuraj Linkeš            timeout: Wait at most this long in seconds for `command` execution to complete.
1296ef07151SJuraj Linkeš            privileged: Whether to run the command with administrative privileges.
1306ef07151SJuraj Linkeš            verify: If :data:`True`, will check the exit code of the command.
1316ef07151SJuraj Linkeš            env: A dictionary with environment variables to be used with the command execution.
1326ef07151SJuraj Linkeš
1336ef07151SJuraj Linkeš        Raises:
1346ef07151SJuraj Linkeš            RemoteCommandExecutionError: If verify is :data:`True` and the command failed.
135840b1e01SJuraj Linkeš        """
136840b1e01SJuraj Linkeš        if privileged:
137840b1e01SJuraj Linkeš            command = self._get_privileged_command(command)
138840b1e01SJuraj Linkeš
139840b1e01SJuraj Linkeš        return self.remote_session.send_command(command, timeout, verify, env)
140840b1e01SJuraj Linkeš
1416e3cdef8SJuraj Linkeš    def close(self) -> None:
1426e3cdef8SJuraj Linkeš        """Close the underlying remote session."""
1436e3cdef8SJuraj Linkeš        self.remote_session.close()
1446e3cdef8SJuraj Linkeš
145840b1e01SJuraj Linkeš    @staticmethod
146840b1e01SJuraj Linkeš    @abstractmethod
147840b1e01SJuraj Linkeš    def _get_privileged_command(command: str) -> str:
148840b1e01SJuraj Linkeš        """Modify the command so that it executes with administrative privileges.
149840b1e01SJuraj Linkeš
150840b1e01SJuraj Linkeš        Args:
151840b1e01SJuraj Linkeš            command: The command to modify.
152840b1e01SJuraj Linkeš
153840b1e01SJuraj Linkeš        Returns:
154840b1e01SJuraj Linkeš            The modified command that executes with administrative privileges.
155840b1e01SJuraj Linkeš        """
156840b1e01SJuraj Linkeš
157840b1e01SJuraj Linkeš    @abstractmethod
158840b1e01SJuraj Linkeš    def get_remote_tmp_dir(self) -> PurePath:
1596ef07151SJuraj Linkeš        """Get the path of the temporary directory of the remote OS.
1606ef07151SJuraj Linkeš
1616ef07151SJuraj Linkeš        Returns:
1626ef07151SJuraj Linkeš            The absolute path of the temporary directory.
163840b1e01SJuraj Linkeš        """
164840b1e01SJuraj Linkeš
165840b1e01SJuraj Linkeš    @abstractmethod
166840b1e01SJuraj Linkeš    def get_dpdk_build_env_vars(self, arch: Architecture) -> dict:
1676ef07151SJuraj Linkeš        """Create extra environment variables needed for the target architecture.
1686ef07151SJuraj Linkeš
1696ef07151SJuraj Linkeš        Different architectures may require different configuration, such as setting 32-bit CFLAGS.
1706ef07151SJuraj Linkeš
1716ef07151SJuraj Linkeš        Returns:
1726ef07151SJuraj Linkeš            A dictionary with keys as environment variables.
173840b1e01SJuraj Linkeš        """
174840b1e01SJuraj Linkeš
175840b1e01SJuraj Linkeš    @abstractmethod
176840b1e01SJuraj Linkeš    def join_remote_path(self, *args: str | PurePath) -> PurePath:
1776ef07151SJuraj Linkeš        """Join path parts using the path separator that fits the remote OS.
1786ef07151SJuraj Linkeš
1796ef07151SJuraj Linkeš        Args:
1806ef07151SJuraj Linkeš            args: Any number of paths to join.
1816ef07151SJuraj Linkeš
1826ef07151SJuraj Linkeš        Returns:
1836ef07151SJuraj Linkeš            The resulting joined path.
184840b1e01SJuraj Linkeš        """
185840b1e01SJuraj Linkeš
186840b1e01SJuraj Linkeš    @abstractmethod
187f9957667STomáš Ďurovec    def remote_path_exists(self, remote_path: str | PurePath) -> bool:
188f9957667STomáš Ďurovec        """Check whether `remote_path` exists on the remote system.
189f9957667STomáš Ďurovec
190f9957667STomáš Ďurovec        Args:
191f9957667STomáš Ďurovec            remote_path: The path to check.
192f9957667STomáš Ďurovec
193f9957667STomáš Ďurovec        Returns:
194f9957667STomáš Ďurovec            :data:`True` if the path exists, :data:`False` otherwise.
195f9957667STomáš Ďurovec        """
196f9957667STomáš Ďurovec
197f9957667STomáš Ďurovec    @abstractmethod
198441c5fbfSTomáš Ďurovec    def copy_from(self, source_file: str | PurePath, destination_dir: str | Path) -> None:
1996ef07151SJuraj Linkeš        """Copy a file from the remote node to the local filesystem.
200840b1e01SJuraj Linkeš
2016ef07151SJuraj Linkeš        Copy `source_file` from the remote node associated with this remote
202441c5fbfSTomáš Ďurovec        session to `destination_dir` on the local filesystem.
203840b1e01SJuraj Linkeš
204840b1e01SJuraj Linkeš        Args:
205441c5fbfSTomáš Ďurovec            source_file: The file on the remote node.
206441c5fbfSTomáš Ďurovec            destination_dir: The directory path on the local filesystem where the `source_file`
207441c5fbfSTomáš Ďurovec                will be saved.
208840b1e01SJuraj Linkeš        """
209840b1e01SJuraj Linkeš
210840b1e01SJuraj Linkeš    @abstractmethod
211441c5fbfSTomáš Ďurovec    def copy_to(self, source_file: str | Path, destination_dir: str | PurePath) -> None:
2126ef07151SJuraj Linkeš        """Copy a file from local filesystem to the remote node.
213840b1e01SJuraj Linkeš
214441c5fbfSTomáš Ďurovec        Copy `source_file` from local filesystem to `destination_dir`
2156ef07151SJuraj Linkeš        on the remote node associated with this remote session.
216840b1e01SJuraj Linkeš
217840b1e01SJuraj Linkeš        Args:
218441c5fbfSTomáš Ďurovec            source_file: The file on the local filesystem.
219441c5fbfSTomáš Ďurovec            destination_dir: The directory path on the remote Node where the `source_file`
220441c5fbfSTomáš Ďurovec                will be saved.
221840b1e01SJuraj Linkeš        """
222840b1e01SJuraj Linkeš
223840b1e01SJuraj Linkeš    @abstractmethod
22480158fd4STomáš Ďurovec    def copy_dir_from(
22580158fd4STomáš Ďurovec        self,
22680158fd4STomáš Ďurovec        source_dir: str | PurePath,
22780158fd4STomáš Ďurovec        destination_dir: str | Path,
22880158fd4STomáš Ďurovec        compress_format: TarCompressionFormat = TarCompressionFormat.none,
22980158fd4STomáš Ďurovec        exclude: str | list[str] | None = None,
23080158fd4STomáš Ďurovec    ) -> None:
23180158fd4STomáš Ďurovec        """Copy a directory from the remote node to the local filesystem.
23280158fd4STomáš Ďurovec
23380158fd4STomáš Ďurovec        Copy `source_dir` from the remote node associated with this remote session to
23480158fd4STomáš Ďurovec        `destination_dir` on the local filesystem. The new local directory will be created
23580158fd4STomáš Ďurovec        at `destination_dir` path.
23680158fd4STomáš Ďurovec
23780158fd4STomáš Ďurovec        Example:
23880158fd4STomáš Ďurovec            source_dir = '/remote/path/to/source'
23980158fd4STomáš Ďurovec            destination_dir = '/local/path/to/destination'
24080158fd4STomáš Ďurovec            compress_format = TarCompressionFormat.xz
24180158fd4STomáš Ďurovec
24280158fd4STomáš Ďurovec            The method will:
24380158fd4STomáš Ďurovec                1. Create a tarball from `source_dir`, resulting in:
24480158fd4STomáš Ďurovec                    '/remote/path/to/source.tar.xz',
24580158fd4STomáš Ďurovec                2. Copy '/remote/path/to/source.tar.xz' to
24680158fd4STomáš Ďurovec                    '/local/path/to/destination/source.tar.xz',
24780158fd4STomáš Ďurovec                3. Extract the contents of the tarball, resulting in:
24880158fd4STomáš Ďurovec                    '/local/path/to/destination/source/',
24980158fd4STomáš Ďurovec                4. Remove the tarball after extraction
25080158fd4STomáš Ďurovec                    ('/local/path/to/destination/source.tar.xz').
25180158fd4STomáš Ďurovec
25280158fd4STomáš Ďurovec            Final Path Structure:
25380158fd4STomáš Ďurovec                '/local/path/to/destination/source/'
25480158fd4STomáš Ďurovec
25580158fd4STomáš Ďurovec        Args:
25680158fd4STomáš Ďurovec            source_dir: The directory on the remote node.
25780158fd4STomáš Ďurovec            destination_dir: The directory path on the local filesystem.
25880158fd4STomáš Ďurovec            compress_format: The compression format to use. Defaults to no compression.
25980158fd4STomáš Ďurovec            exclude: Patterns for files or directories to exclude from the tarball.
26080158fd4STomáš Ďurovec                These patterns are used with `tar`'s `--exclude` option.
26180158fd4STomáš Ďurovec        """
26280158fd4STomáš Ďurovec
26380158fd4STomáš Ďurovec    @abstractmethod
26480158fd4STomáš Ďurovec    def copy_dir_to(
26580158fd4STomáš Ďurovec        self,
26680158fd4STomáš Ďurovec        source_dir: str | Path,
26780158fd4STomáš Ďurovec        destination_dir: str | PurePath,
26880158fd4STomáš Ďurovec        compress_format: TarCompressionFormat = TarCompressionFormat.none,
26980158fd4STomáš Ďurovec        exclude: str | list[str] | None = None,
27080158fd4STomáš Ďurovec    ) -> None:
27180158fd4STomáš Ďurovec        """Copy a directory from the local filesystem to the remote node.
27280158fd4STomáš Ďurovec
27380158fd4STomáš Ďurovec        Copy `source_dir` from the local filesystem to `destination_dir` on the remote node
27480158fd4STomáš Ďurovec        associated with this remote session. The new remote directory will be created at
27580158fd4STomáš Ďurovec        `destination_dir` path.
27680158fd4STomáš Ďurovec
27780158fd4STomáš Ďurovec        Example:
27880158fd4STomáš Ďurovec            source_dir = '/local/path/to/source'
27980158fd4STomáš Ďurovec            destination_dir = '/remote/path/to/destination'
28080158fd4STomáš Ďurovec            compress_format = TarCompressionFormat.xz
28180158fd4STomáš Ďurovec
28280158fd4STomáš Ďurovec            The method will:
28380158fd4STomáš Ďurovec                1. Create a tarball from `source_dir`, resulting in:
28480158fd4STomáš Ďurovec                    '/local/path/to/source.tar.xz',
28580158fd4STomáš Ďurovec                2. Copy '/local/path/to/source.tar.xz' to
28680158fd4STomáš Ďurovec                    '/remote/path/to/destination/source.tar.xz',
28780158fd4STomáš Ďurovec                3. Extract the contents of the tarball, resulting in:
28880158fd4STomáš Ďurovec                    '/remote/path/to/destination/source/',
28980158fd4STomáš Ďurovec                4. Remove the tarball after extraction
29080158fd4STomáš Ďurovec                    ('/remote/path/to/destination/source.tar.xz').
29180158fd4STomáš Ďurovec
29280158fd4STomáš Ďurovec            Final Path Structure:
29380158fd4STomáš Ďurovec                '/remote/path/to/destination/source/'
29480158fd4STomáš Ďurovec
29580158fd4STomáš Ďurovec        Args:
29680158fd4STomáš Ďurovec            source_dir: The directory on the local filesystem.
29780158fd4STomáš Ďurovec            destination_dir: The directory path on the remote node.
29880158fd4STomáš Ďurovec            compress_format: The compression format to use. Defaults to no compression.
29980158fd4STomáš Ďurovec            exclude: Patterns for files or directories to exclude from the tarball.
30080158fd4STomáš Ďurovec                These patterns are used with `fnmatch.fnmatch` to filter out files.
30180158fd4STomáš Ďurovec        """
30280158fd4STomáš Ďurovec
30380158fd4STomáš Ďurovec    @abstractmethod
30480158fd4STomáš Ďurovec    def remove_remote_file(self, remote_file_path: str | PurePath, force: bool = True) -> None:
30580158fd4STomáš Ďurovec        """Remove remote file, by default remove forcefully.
30680158fd4STomáš Ďurovec
30780158fd4STomáš Ďurovec        Args:
30880158fd4STomáš Ďurovec            remote_file_path: The file path to remove.
30980158fd4STomáš Ďurovec            force: If :data:`True`, ignore all warnings and try to remove at all costs.
31080158fd4STomáš Ďurovec        """
31180158fd4STomáš Ďurovec
31280158fd4STomáš Ďurovec    @abstractmethod
313840b1e01SJuraj Linkeš    def remove_remote_dir(
314840b1e01SJuraj Linkeš        self,
315840b1e01SJuraj Linkeš        remote_dir_path: str | PurePath,
316840b1e01SJuraj Linkeš        recursive: bool = True,
317840b1e01SJuraj Linkeš        force: bool = True,
318840b1e01SJuraj Linkeš    ) -> None:
3196ef07151SJuraj Linkeš        """Remove remote directory, by default remove recursively and forcefully.
3206ef07151SJuraj Linkeš
3216ef07151SJuraj Linkeš        Args:
32280158fd4STomáš Ďurovec            remote_dir_path: The directory path to remove.
3236ef07151SJuraj Linkeš            recursive: If :data:`True`, also remove all contents inside the directory.
3246ef07151SJuraj Linkeš            force: If :data:`True`, ignore all warnings and try to remove at all costs.
325840b1e01SJuraj Linkeš        """
326840b1e01SJuraj Linkeš
327840b1e01SJuraj Linkeš    @abstractmethod
32880158fd4STomáš Ďurovec    def create_remote_tarball(
32980158fd4STomáš Ďurovec        self,
33080158fd4STomáš Ďurovec        remote_dir_path: str | PurePath,
33180158fd4STomáš Ďurovec        compress_format: TarCompressionFormat = TarCompressionFormat.none,
33280158fd4STomáš Ďurovec        exclude: str | list[str] | None = None,
33380158fd4STomáš Ďurovec    ) -> PurePosixPath:
33480158fd4STomáš Ďurovec        """Create a tarball from the contents of the specified remote directory.
33580158fd4STomáš Ďurovec
33680158fd4STomáš Ďurovec        This method creates a tarball containing all files and directories
33780158fd4STomáš Ďurovec        within `remote_dir_path`. The tarball will be saved in the directory of
33880158fd4STomáš Ďurovec        `remote_dir_path` and will be named based on `remote_dir_path`.
33980158fd4STomáš Ďurovec
34080158fd4STomáš Ďurovec        Args:
34180158fd4STomáš Ďurovec            remote_dir_path: The directory path on the remote node.
34280158fd4STomáš Ďurovec            compress_format: The compression format to use. Defaults to no compression.
34380158fd4STomáš Ďurovec            exclude: Patterns for files or directories to exclude from the tarball.
34480158fd4STomáš Ďurovec                These patterns are used with `tar`'s `--exclude` option.
34580158fd4STomáš Ďurovec
34680158fd4STomáš Ďurovec        Returns:
34780158fd4STomáš Ďurovec            The path to the created tarball on the remote node.
34880158fd4STomáš Ďurovec        """
34980158fd4STomáš Ďurovec
35080158fd4STomáš Ďurovec    @abstractmethod
351840b1e01SJuraj Linkeš    def extract_remote_tarball(
352840b1e01SJuraj Linkeš        self,
353840b1e01SJuraj Linkeš        remote_tarball_path: str | PurePath,
354840b1e01SJuraj Linkeš        expected_dir: str | PurePath | None = None,
355840b1e01SJuraj Linkeš    ) -> None:
3566ef07151SJuraj Linkeš        """Extract remote tarball in its remote directory.
3576ef07151SJuraj Linkeš
3586ef07151SJuraj Linkeš        Args:
35980158fd4STomáš Ďurovec            remote_tarball_path: The tarball path on the remote node.
3606ef07151SJuraj Linkeš            expected_dir: If non-empty, check whether `expected_dir` exists after extracting
3616ef07151SJuraj Linkeš                the archive.
362840b1e01SJuraj Linkeš        """
363840b1e01SJuraj Linkeš
364840b1e01SJuraj Linkeš    @abstractmethod
365*b935bdc3SLuca Vizzarro    def is_remote_dir(self, remote_path: PurePath) -> bool:
366f9957667STomáš Ďurovec        """Check if the `remote_path` is a directory.
367f9957667STomáš Ďurovec
368f9957667STomáš Ďurovec        Args:
369f9957667STomáš Ďurovec            remote_tarball_path: The path to the remote tarball.
370f9957667STomáš Ďurovec
371f9957667STomáš Ďurovec        Returns:
372f9957667STomáš Ďurovec            If :data:`True` the `remote_path` is a directory, otherwise :data:`False`.
373f9957667STomáš Ďurovec        """
374f9957667STomáš Ďurovec
375f9957667STomáš Ďurovec    @abstractmethod
376*b935bdc3SLuca Vizzarro    def is_remote_tarfile(self, remote_tarball_path: PurePath) -> bool:
377f9957667STomáš Ďurovec        """Check if the `remote_tarball_path` is a tar archive.
378f9957667STomáš Ďurovec
379f9957667STomáš Ďurovec        Args:
380f9957667STomáš Ďurovec            remote_tarball_path: The path to the remote tarball.
381f9957667STomáš Ďurovec
382f9957667STomáš Ďurovec        Returns:
383f9957667STomáš Ďurovec            If :data:`True` the `remote_tarball_path` is a tar archive, otherwise :data:`False`.
384f9957667STomáš Ďurovec        """
385f9957667STomáš Ďurovec
386f9957667STomáš Ďurovec    @abstractmethod
387f9957667STomáš Ďurovec    def get_tarball_top_dir(
388f9957667STomáš Ďurovec        self, remote_tarball_path: str | PurePath
389f9957667STomáš Ďurovec    ) -> str | PurePosixPath | None:
390f9957667STomáš Ďurovec        """Get the top directory of the remote tarball.
391f9957667STomáš Ďurovec
392f9957667STomáš Ďurovec        Examines the contents of a tarball located at the given `remote_tarball_path` and
393f9957667STomáš Ďurovec        determines the top-level directory. If all files and directories in the tarball share
394f9957667STomáš Ďurovec        the same top-level directory, that directory name is returned. If the tarball contains
395f9957667STomáš Ďurovec        multiple top-level directories or is empty, the method return None.
396f9957667STomáš Ďurovec
397f9957667STomáš Ďurovec        Args:
398f9957667STomáš Ďurovec            remote_tarball_path: The path to the remote tarball.
399f9957667STomáš Ďurovec
400f9957667STomáš Ďurovec        Returns:
401f9957667STomáš Ďurovec            The top directory of the tarball. If there are multiple top directories
402f9957667STomáš Ďurovec            or the tarball is empty, returns :data:`None`.
403f9957667STomáš Ďurovec        """
404f9957667STomáš Ďurovec
405f9957667STomáš Ďurovec    @abstractmethod
406840b1e01SJuraj Linkeš    def build_dpdk(
407840b1e01SJuraj Linkeš        self,
408840b1e01SJuraj Linkeš        env_vars: dict,
409840b1e01SJuraj Linkeš        meson_args: MesonArgs,
410840b1e01SJuraj Linkeš        remote_dpdk_dir: str | PurePath,
411840b1e01SJuraj Linkeš        remote_dpdk_build_dir: str | PurePath,
412840b1e01SJuraj Linkeš        rebuild: bool = False,
413840b1e01SJuraj Linkeš        timeout: float = SETTINGS.compile_timeout,
414840b1e01SJuraj Linkeš    ) -> None:
4156ef07151SJuraj Linkeš        """Build DPDK on the remote node.
4166ef07151SJuraj Linkeš
4176ef07151SJuraj Linkeš        An extracted DPDK tarball must be present on the node. The build consists of two steps::
4186ef07151SJuraj Linkeš
4196ef07151SJuraj Linkeš            meson setup <meson args> remote_dpdk_dir remote_dpdk_build_dir
4206ef07151SJuraj Linkeš            ninja -C remote_dpdk_build_dir
4216ef07151SJuraj Linkeš
4226ef07151SJuraj Linkeš        The :option:`--compile-timeout` command line argument and the :envvar:`DTS_COMPILE_TIMEOUT`
4236ef07151SJuraj Linkeš        environment variable configure the timeout of DPDK build.
4246ef07151SJuraj Linkeš
4256ef07151SJuraj Linkeš        Args:
4266ef07151SJuraj Linkeš            env_vars: Use these environment variables when building DPDK.
4276ef07151SJuraj Linkeš            meson_args: Use these meson arguments when building DPDK.
4286ef07151SJuraj Linkeš            remote_dpdk_dir: The directory on the remote node where DPDK will be built.
4296ef07151SJuraj Linkeš            remote_dpdk_build_dir: The target build directory on the remote node.
4306ef07151SJuraj Linkeš            rebuild: If :data:`True`, do a subsequent build with ``meson configure`` instead
4316ef07151SJuraj Linkeš                of ``meson setup``.
4326ef07151SJuraj Linkeš            timeout: Wait at most this long in seconds for the build execution to complete.
433840b1e01SJuraj Linkeš        """
434840b1e01SJuraj Linkeš
435840b1e01SJuraj Linkeš    @abstractmethod
436840b1e01SJuraj Linkeš    def get_dpdk_version(self, version_path: str | PurePath) -> str:
4376ef07151SJuraj Linkeš        """Inspect the DPDK version on the remote node.
4386ef07151SJuraj Linkeš
4396ef07151SJuraj Linkeš        Args:
4406ef07151SJuraj Linkeš            version_path: The path to the VERSION file containing the DPDK version.
4416ef07151SJuraj Linkeš
4426ef07151SJuraj Linkeš        Returns:
4436ef07151SJuraj Linkeš            The DPDK version.
444840b1e01SJuraj Linkeš        """
445840b1e01SJuraj Linkeš
446840b1e01SJuraj Linkeš    @abstractmethod
447840b1e01SJuraj Linkeš    def get_remote_cpus(self, use_first_core: bool) -> list[LogicalCore]:
4486ef07151SJuraj Linkeš        r"""Get the list of :class:`~.cpu.LogicalCore`\s on the remote node.
4496ef07151SJuraj Linkeš
4506ef07151SJuraj Linkeš        Args:
4516ef07151SJuraj Linkeš            use_first_core: If :data:`False`, the first physical core won't be used.
4526ef07151SJuraj Linkeš
4536ef07151SJuraj Linkeš        Returns:
4546ef07151SJuraj Linkeš            The logical cores present on the node.
455840b1e01SJuraj Linkeš        """
456840b1e01SJuraj Linkeš
457840b1e01SJuraj Linkeš    @abstractmethod
458840b1e01SJuraj Linkeš    def kill_cleanup_dpdk_apps(self, dpdk_prefix_list: Iterable[str]) -> None:
4596ef07151SJuraj Linkeš        """Kill and cleanup all DPDK apps.
4606ef07151SJuraj Linkeš
4616ef07151SJuraj Linkeš        Args:
4626ef07151SJuraj Linkeš            dpdk_prefix_list: Kill all apps identified by `dpdk_prefix_list`.
4636ef07151SJuraj Linkeš                If `dpdk_prefix_list` is empty, attempt to find running DPDK apps to kill and clean.
464840b1e01SJuraj Linkeš        """
465840b1e01SJuraj Linkeš
466840b1e01SJuraj Linkeš    @abstractmethod
467840b1e01SJuraj Linkeš    def get_dpdk_file_prefix(self, dpdk_prefix: str) -> str:
4686ef07151SJuraj Linkeš        """Make OS-specific modification to the DPDK file prefix.
4696ef07151SJuraj Linkeš
4706ef07151SJuraj Linkeš        Args:
4716ef07151SJuraj Linkeš           dpdk_prefix: The OS-unaware file prefix.
4726ef07151SJuraj Linkeš
4736ef07151SJuraj Linkeš        Returns:
4746ef07151SJuraj Linkeš            The OS-specific file prefix.
475840b1e01SJuraj Linkeš        """
476840b1e01SJuraj Linkeš
477840b1e01SJuraj Linkeš    @abstractmethod
478c0dd39deSNicholas Pratte    def setup_hugepages(self, number_of: int, hugepage_size: int, force_first_numa: bool) -> None:
4796ef07151SJuraj Linkeš        """Configure hugepages on the node.
4806ef07151SJuraj Linkeš
4816ef07151SJuraj Linkeš        Get the node's Hugepage Size, configure the specified count of hugepages
482840b1e01SJuraj Linkeš        if needed and mount the hugepages if needed.
4836ef07151SJuraj Linkeš
4846ef07151SJuraj Linkeš        Args:
485c0dd39deSNicholas Pratte            number_of: Configure this many hugepages.
486e5307b25SNicholas Pratte            hugepage_size: Configure hugepages of this size.
487c0dd39deSNicholas Pratte            force_first_numa:  If :data:`True`, configure just on the first numa node.
488840b1e01SJuraj Linkeš        """
489840b1e01SJuraj Linkeš
490840b1e01SJuraj Linkeš    @abstractmethod
491840b1e01SJuraj Linkeš    def get_compiler_version(self, compiler_name: str) -> str:
4926ef07151SJuraj Linkeš        """Get installed version of compiler used for DPDK.
4936ef07151SJuraj Linkeš
4946ef07151SJuraj Linkeš        Args:
4956ef07151SJuraj Linkeš            compiler_name: The name of the compiler executable.
4966ef07151SJuraj Linkeš
4976ef07151SJuraj Linkeš        Returns:
4986ef07151SJuraj Linkeš            The compiler's version.
499840b1e01SJuraj Linkeš        """
500840b1e01SJuraj Linkeš
501840b1e01SJuraj Linkeš    @abstractmethod
502c72ff85dSLuca Vizzarro    def get_node_info(self) -> OSSessionInfo:
5036ef07151SJuraj Linkeš        """Collect additional information about the node.
5046ef07151SJuraj Linkeš
5056ef07151SJuraj Linkeš        Returns:
5066ef07151SJuraj Linkeš            Node information.
507840b1e01SJuraj Linkeš        """
508840b1e01SJuraj Linkeš
509840b1e01SJuraj Linkeš    @abstractmethod
510840b1e01SJuraj Linkeš    def update_ports(self, ports: list[Port]) -> None:
5116ef07151SJuraj Linkeš        """Get additional information about ports from the operating system and update them.
5126ef07151SJuraj Linkeš
5136ef07151SJuraj Linkeš        The additional information is:
5146ef07151SJuraj Linkeš
5156ef07151SJuraj Linkeš            * Logical name (e.g. ``enp7s0``) if applicable,
5166ef07151SJuraj Linkeš            * Mac address.
5176ef07151SJuraj Linkeš
5186ef07151SJuraj Linkeš        Args:
5196ef07151SJuraj Linkeš            ports: The ports to update.
520840b1e01SJuraj Linkeš        """
521840b1e01SJuraj Linkeš
522840b1e01SJuraj Linkeš    @abstractmethod
523ace78563SJeremy Spewock    def configure_port_mtu(self, mtu: int, port: Port) -> None:
524ace78563SJeremy Spewock        """Configure `mtu` on `port`.
525ace78563SJeremy Spewock
526ace78563SJeremy Spewock        Args:
527ace78563SJeremy Spewock            mtu: Desired MTU value.
528ace78563SJeremy Spewock            port: Port to set `mtu` on.
529ace78563SJeremy Spewock        """
530