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