1c4ef44deSJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause 2c4ef44deSJuraj Linkeš# Copyright(c) 2010-2014 Intel Corporation 378534506SJuraj Linkeš# Copyright(c) 2022-2023 PANTHEON.tech s.r.o. 478534506SJuraj Linkeš# Copyright(c) 2022-2023 University of New Hampshire 5c4ef44deSJuraj Linkeš 6c4ef44deSJuraj Linkeš""" 7c4ef44deSJuraj LinkešA node is a generic host that DTS connects to and manages. 8c4ef44deSJuraj Linkeš""" 9c4ef44deSJuraj Linkeš 10680d8a24SJuraj Linkešfrom typing import Any, Callable 11680d8a24SJuraj Linkeš 1278534506SJuraj Linkešfrom framework.config import ( 1378534506SJuraj Linkeš BuildTargetConfiguration, 1478534506SJuraj Linkeš ExecutionConfiguration, 1578534506SJuraj Linkeš NodeConfiguration, 1678534506SJuraj Linkeš) 17c4ef44deSJuraj Linkešfrom framework.logger import DTSLOG, getLogger 1878534506SJuraj Linkešfrom framework.remote_session import OSSession, create_session 19680d8a24SJuraj Linkešfrom framework.settings import SETTINGS 20c4ef44deSJuraj Linkeš 21c020b7ceSJuraj Linkešfrom .hw import ( 22c020b7ceSJuraj Linkeš LogicalCore, 23c020b7ceSJuraj Linkeš LogicalCoreCount, 24c020b7ceSJuraj Linkeš LogicalCoreList, 25c020b7ceSJuraj Linkeš LogicalCoreListFilter, 26c020b7ceSJuraj Linkeš lcore_filter, 27c020b7ceSJuraj Linkeš) 28c020b7ceSJuraj Linkeš 29c4ef44deSJuraj Linkeš 30c4ef44deSJuraj Linkešclass Node(object): 31c4ef44deSJuraj Linkeš """ 3278534506SJuraj Linkeš Basic class for node management. This class implements methods that 33c4ef44deSJuraj Linkeš manage a node, such as information gathering (of CPU/PCI/NIC) and 34c4ef44deSJuraj Linkeš environment setup. 35c4ef44deSJuraj Linkeš """ 36c4ef44deSJuraj Linkeš 3778534506SJuraj Linkeš main_session: OSSession 3878534506SJuraj Linkeš config: NodeConfiguration 39c4ef44deSJuraj Linkeš name: str 40c020b7ceSJuraj Linkeš lcores: list[LogicalCore] 4178534506SJuraj Linkeš _logger: DTSLOG 4278534506SJuraj Linkeš _other_sessions: list[OSSession] 43c4ef44deSJuraj Linkeš 44c4ef44deSJuraj Linkeš def __init__(self, node_config: NodeConfiguration): 4578534506SJuraj Linkeš self.config = node_config 4678534506SJuraj Linkeš self.name = node_config.name 4778534506SJuraj Linkeš self._logger = getLogger(self.name) 4878534506SJuraj Linkeš self.main_session = create_session(self.config, self.name, self._logger) 4978534506SJuraj Linkeš 50c020b7ceSJuraj Linkeš self._get_remote_cpus() 51c020b7ceSJuraj Linkeš # filter the node lcores according to user config 52c020b7ceSJuraj Linkeš self.lcores = LogicalCoreListFilter( 53c020b7ceSJuraj Linkeš self.lcores, LogicalCoreList(self.config.lcores) 54c020b7ceSJuraj Linkeš ).filter() 55c020b7ceSJuraj Linkeš 56c4ef44deSJuraj Linkeš self._other_sessions = [] 57c4ef44deSJuraj Linkeš 5878534506SJuraj Linkeš self._logger.info(f"Created node: {self.name}") 59c4ef44deSJuraj Linkeš 6078534506SJuraj Linkeš def set_up_execution(self, execution_config: ExecutionConfiguration) -> None: 61c4ef44deSJuraj Linkeš """ 6278534506SJuraj Linkeš Perform the execution setup that will be done for each execution 6378534506SJuraj Linkeš this node is part of. 6478534506SJuraj Linkeš """ 65*b76d80a4SJuraj Linkeš self._setup_hugepages() 6678534506SJuraj Linkeš self._set_up_execution(execution_config) 6778534506SJuraj Linkeš 6878534506SJuraj Linkeš def _set_up_execution(self, execution_config: ExecutionConfiguration) -> None: 6978534506SJuraj Linkeš """ 7078534506SJuraj Linkeš This method exists to be optionally overwritten by derived classes and 7178534506SJuraj Linkeš is not decorated so that the derived class doesn't have to use the decorator. 72c4ef44deSJuraj Linkeš """ 73c4ef44deSJuraj Linkeš 7478534506SJuraj Linkeš def tear_down_execution(self) -> None: 7578534506SJuraj Linkeš """ 7678534506SJuraj Linkeš Perform the execution teardown that will be done after each execution 7778534506SJuraj Linkeš this node is part of concludes. 7878534506SJuraj Linkeš """ 7978534506SJuraj Linkeš self._tear_down_execution() 80c4ef44deSJuraj Linkeš 8178534506SJuraj Linkeš def _tear_down_execution(self) -> None: 8278534506SJuraj Linkeš """ 8378534506SJuraj Linkeš This method exists to be optionally overwritten by derived classes and 8478534506SJuraj Linkeš is not decorated so that the derived class doesn't have to use the decorator. 8578534506SJuraj Linkeš """ 8678534506SJuraj Linkeš 8778534506SJuraj Linkeš def set_up_build_target( 8878534506SJuraj Linkeš self, build_target_config: BuildTargetConfiguration 8978534506SJuraj Linkeš ) -> None: 9078534506SJuraj Linkeš """ 9178534506SJuraj Linkeš Perform the build target setup that will be done for each build target 9278534506SJuraj Linkeš tested on this node. 9378534506SJuraj Linkeš """ 9478534506SJuraj Linkeš self._set_up_build_target(build_target_config) 9578534506SJuraj Linkeš 9678534506SJuraj Linkeš def _set_up_build_target( 9778534506SJuraj Linkeš self, build_target_config: BuildTargetConfiguration 9878534506SJuraj Linkeš ) -> None: 9978534506SJuraj Linkeš """ 10078534506SJuraj Linkeš This method exists to be optionally overwritten by derived classes and 10178534506SJuraj Linkeš is not decorated so that the derived class doesn't have to use the decorator. 10278534506SJuraj Linkeš """ 10378534506SJuraj Linkeš 10478534506SJuraj Linkeš def tear_down_build_target(self) -> None: 10578534506SJuraj Linkeš """ 10678534506SJuraj Linkeš Perform the build target teardown that will be done after each build target 10778534506SJuraj Linkeš tested on this node. 10878534506SJuraj Linkeš """ 10978534506SJuraj Linkeš self._tear_down_build_target() 11078534506SJuraj Linkeš 11178534506SJuraj Linkeš def _tear_down_build_target(self) -> None: 11278534506SJuraj Linkeš """ 11378534506SJuraj Linkeš This method exists to be optionally overwritten by derived classes and 11478534506SJuraj Linkeš is not decorated so that the derived class doesn't have to use the decorator. 11578534506SJuraj Linkeš """ 11678534506SJuraj Linkeš 11778534506SJuraj Linkeš def create_session(self, name: str) -> OSSession: 11878534506SJuraj Linkeš """ 11978534506SJuraj Linkeš Create and return a new OSSession tailored to the remote OS. 12078534506SJuraj Linkeš """ 12178534506SJuraj Linkeš session_name = f"{self.name} {name}" 12278534506SJuraj Linkeš connection = create_session( 12378534506SJuraj Linkeš self.config, 12478534506SJuraj Linkeš session_name, 12578534506SJuraj Linkeš getLogger(session_name, node=self.name), 126c4ef44deSJuraj Linkeš ) 127c4ef44deSJuraj Linkeš self._other_sessions.append(connection) 128c4ef44deSJuraj Linkeš return connection 129c4ef44deSJuraj Linkeš 130c020b7ceSJuraj Linkeš def filter_lcores( 131c020b7ceSJuraj Linkeš self, 132c020b7ceSJuraj Linkeš filter_specifier: LogicalCoreCount | LogicalCoreList, 133c020b7ceSJuraj Linkeš ascending: bool = True, 134c020b7ceSJuraj Linkeš ) -> list[LogicalCore]: 135c020b7ceSJuraj Linkeš """ 136c020b7ceSJuraj Linkeš Filter the LogicalCores found on the Node according to 137c020b7ceSJuraj Linkeš a LogicalCoreCount or a LogicalCoreList. 138c020b7ceSJuraj Linkeš 139c020b7ceSJuraj Linkeš If ascending is True, use cores with the lowest numerical id first 140c020b7ceSJuraj Linkeš and continue in ascending order. If False, start with the highest 141c020b7ceSJuraj Linkeš id and continue in descending order. This ordering affects which 142c020b7ceSJuraj Linkeš sockets to consider first as well. 143c020b7ceSJuraj Linkeš """ 144c020b7ceSJuraj Linkeš self._logger.debug(f"Filtering {filter_specifier} from {self.lcores}.") 145c020b7ceSJuraj Linkeš return lcore_filter( 146c020b7ceSJuraj Linkeš self.lcores, 147c020b7ceSJuraj Linkeš filter_specifier, 148c020b7ceSJuraj Linkeš ascending, 149c020b7ceSJuraj Linkeš ).filter() 150c020b7ceSJuraj Linkeš 151c020b7ceSJuraj Linkeš def _get_remote_cpus(self) -> None: 152c020b7ceSJuraj Linkeš """ 153c020b7ceSJuraj Linkeš Scan CPUs in the remote OS and store a list of LogicalCores. 154c020b7ceSJuraj Linkeš """ 155c020b7ceSJuraj Linkeš self._logger.info("Getting CPU information.") 156c020b7ceSJuraj Linkeš self.lcores = self.main_session.get_remote_cpus(self.config.use_first_core) 157c020b7ceSJuraj Linkeš 158*b76d80a4SJuraj Linkeš def _setup_hugepages(self): 159*b76d80a4SJuraj Linkeš """ 160*b76d80a4SJuraj Linkeš Setup hugepages on the Node. Different architectures can supply different 161*b76d80a4SJuraj Linkeš amounts of memory for hugepages and numa-based hugepage allocation may need 162*b76d80a4SJuraj Linkeš to be considered. 163*b76d80a4SJuraj Linkeš """ 164*b76d80a4SJuraj Linkeš if self.config.hugepages: 165*b76d80a4SJuraj Linkeš self.main_session.setup_hugepages( 166*b76d80a4SJuraj Linkeš self.config.hugepages.amount, self.config.hugepages.force_first_numa 167*b76d80a4SJuraj Linkeš ) 168*b76d80a4SJuraj Linkeš 16978534506SJuraj Linkeš def close(self) -> None: 170c4ef44deSJuraj Linkeš """ 17178534506SJuraj Linkeš Close all connections and free other resources. 172c4ef44deSJuraj Linkeš """ 173c4ef44deSJuraj Linkeš if self.main_session: 174c4ef44deSJuraj Linkeš self.main_session.close() 175c4ef44deSJuraj Linkeš for session in self._other_sessions: 176c4ef44deSJuraj Linkeš session.close() 17778534506SJuraj Linkeš self._logger.logger_exit() 178680d8a24SJuraj Linkeš 179680d8a24SJuraj Linkeš @staticmethod 180680d8a24SJuraj Linkeš def skip_setup(func: Callable[..., Any]) -> Callable[..., Any]: 181680d8a24SJuraj Linkeš if SETTINGS.skip_setup: 182680d8a24SJuraj Linkeš return lambda *args: None 183680d8a24SJuraj Linkeš else: 184680d8a24SJuraj Linkeš return func 185