xref: /dpdk/dts/framework/testbed_model/node.py (revision b76d80a4dbb900ac50a8ab3a0cae9996bcd9b8d0)
1# SPDX-License-Identifier: BSD-3-Clause
2# Copyright(c) 2010-2014 Intel Corporation
3# Copyright(c) 2022-2023 PANTHEON.tech s.r.o.
4# Copyright(c) 2022-2023 University of New Hampshire
5
6"""
7A node is a generic host that DTS connects to and manages.
8"""
9
10from typing import Any, Callable
11
12from framework.config import (
13    BuildTargetConfiguration,
14    ExecutionConfiguration,
15    NodeConfiguration,
16)
17from framework.logger import DTSLOG, getLogger
18from framework.remote_session import OSSession, create_session
19from framework.settings import SETTINGS
20
21from .hw import (
22    LogicalCore,
23    LogicalCoreCount,
24    LogicalCoreList,
25    LogicalCoreListFilter,
26    lcore_filter,
27)
28
29
30class Node(object):
31    """
32    Basic class for node management. This class implements methods that
33    manage a node, such as information gathering (of CPU/PCI/NIC) and
34    environment setup.
35    """
36
37    main_session: OSSession
38    config: NodeConfiguration
39    name: str
40    lcores: list[LogicalCore]
41    _logger: DTSLOG
42    _other_sessions: list[OSSession]
43
44    def __init__(self, node_config: NodeConfiguration):
45        self.config = node_config
46        self.name = node_config.name
47        self._logger = getLogger(self.name)
48        self.main_session = create_session(self.config, self.name, self._logger)
49
50        self._get_remote_cpus()
51        # filter the node lcores according to user config
52        self.lcores = LogicalCoreListFilter(
53            self.lcores, LogicalCoreList(self.config.lcores)
54        ).filter()
55
56        self._other_sessions = []
57
58        self._logger.info(f"Created node: {self.name}")
59
60    def set_up_execution(self, execution_config: ExecutionConfiguration) -> None:
61        """
62        Perform the execution setup that will be done for each execution
63        this node is part of.
64        """
65        self._setup_hugepages()
66        self._set_up_execution(execution_config)
67
68    def _set_up_execution(self, execution_config: ExecutionConfiguration) -> None:
69        """
70        This method exists to be optionally overwritten by derived classes and
71        is not decorated so that the derived class doesn't have to use the decorator.
72        """
73
74    def tear_down_execution(self) -> None:
75        """
76        Perform the execution teardown that will be done after each execution
77        this node is part of concludes.
78        """
79        self._tear_down_execution()
80
81    def _tear_down_execution(self) -> None:
82        """
83        This method exists to be optionally overwritten by derived classes and
84        is not decorated so that the derived class doesn't have to use the decorator.
85        """
86
87    def set_up_build_target(
88        self, build_target_config: BuildTargetConfiguration
89    ) -> None:
90        """
91        Perform the build target setup that will be done for each build target
92        tested on this node.
93        """
94        self._set_up_build_target(build_target_config)
95
96    def _set_up_build_target(
97        self, build_target_config: BuildTargetConfiguration
98    ) -> None:
99        """
100        This method exists to be optionally overwritten by derived classes and
101        is not decorated so that the derived class doesn't have to use the decorator.
102        """
103
104    def tear_down_build_target(self) -> None:
105        """
106        Perform the build target teardown that will be done after each build target
107        tested on this node.
108        """
109        self._tear_down_build_target()
110
111    def _tear_down_build_target(self) -> None:
112        """
113        This method exists to be optionally overwritten by derived classes and
114        is not decorated so that the derived class doesn't have to use the decorator.
115        """
116
117    def create_session(self, name: str) -> OSSession:
118        """
119        Create and return a new OSSession tailored to the remote OS.
120        """
121        session_name = f"{self.name} {name}"
122        connection = create_session(
123            self.config,
124            session_name,
125            getLogger(session_name, node=self.name),
126        )
127        self._other_sessions.append(connection)
128        return connection
129
130    def filter_lcores(
131        self,
132        filter_specifier: LogicalCoreCount | LogicalCoreList,
133        ascending: bool = True,
134    ) -> list[LogicalCore]:
135        """
136        Filter the LogicalCores found on the Node according to
137        a LogicalCoreCount or a LogicalCoreList.
138
139        If ascending is True, use cores with the lowest numerical id first
140        and continue in ascending order. If False, start with the highest
141        id and continue in descending order. This ordering affects which
142        sockets to consider first as well.
143        """
144        self._logger.debug(f"Filtering {filter_specifier} from {self.lcores}.")
145        return lcore_filter(
146            self.lcores,
147            filter_specifier,
148            ascending,
149        ).filter()
150
151    def _get_remote_cpus(self) -> None:
152        """
153        Scan CPUs in the remote OS and store a list of LogicalCores.
154        """
155        self._logger.info("Getting CPU information.")
156        self.lcores = self.main_session.get_remote_cpus(self.config.use_first_core)
157
158    def _setup_hugepages(self):
159        """
160        Setup hugepages on the Node. Different architectures can supply different
161        amounts of memory for hugepages and numa-based hugepage allocation may need
162        to be considered.
163        """
164        if self.config.hugepages:
165            self.main_session.setup_hugepages(
166                self.config.hugepages.amount, self.config.hugepages.force_first_numa
167            )
168
169    def close(self) -> None:
170        """
171        Close all connections and free other resources.
172        """
173        if self.main_session:
174            self.main_session.close()
175        for session in self._other_sessions:
176            session.close()
177        self._logger.logger_exit()
178
179    @staticmethod
180    def skip_setup(func: Callable[..., Any]) -> Callable[..., Any]:
181        if SETTINGS.skip_setup:
182            return lambda *args: None
183        else:
184            return func
185