amd-node-scraper 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- amd_node_scraper-0.0.1.dist-info/LICENSE +21 -0
- amd_node_scraper-0.0.1.dist-info/METADATA +424 -0
- amd_node_scraper-0.0.1.dist-info/RECORD +197 -0
- amd_node_scraper-0.0.1.dist-info/WHEEL +5 -0
- amd_node_scraper-0.0.1.dist-info/entry_points.txt +2 -0
- amd_node_scraper-0.0.1.dist-info/top_level.txt +1 -0
- nodescraper/__init__.py +32 -0
- nodescraper/base/__init__.py +34 -0
- nodescraper/base/inbandcollectortask.py +118 -0
- nodescraper/base/inbanddataplugin.py +39 -0
- nodescraper/base/regexanalyzer.py +120 -0
- nodescraper/cli/__init__.py +29 -0
- nodescraper/cli/cli.py +511 -0
- nodescraper/cli/constants.py +27 -0
- nodescraper/cli/dynamicparserbuilder.py +171 -0
- nodescraper/cli/helper.py +517 -0
- nodescraper/cli/inputargtypes.py +129 -0
- nodescraper/configbuilder.py +123 -0
- nodescraper/configregistry.py +66 -0
- nodescraper/configs/node_status.json +19 -0
- nodescraper/connection/__init__.py +25 -0
- nodescraper/connection/inband/__init__.py +46 -0
- nodescraper/connection/inband/inband.py +171 -0
- nodescraper/connection/inband/inbandlocal.py +93 -0
- nodescraper/connection/inband/inbandmanager.py +151 -0
- nodescraper/connection/inband/inbandremote.py +173 -0
- nodescraper/connection/inband/sshparams.py +43 -0
- nodescraper/constants.py +26 -0
- nodescraper/enums/__init__.py +40 -0
- nodescraper/enums/eventcategory.py +89 -0
- nodescraper/enums/eventpriority.py +42 -0
- nodescraper/enums/executionstatus.py +44 -0
- nodescraper/enums/osfamily.py +34 -0
- nodescraper/enums/systeminteraction.py +41 -0
- nodescraper/enums/systemlocation.py +33 -0
- nodescraper/generictypes.py +36 -0
- nodescraper/interfaces/__init__.py +44 -0
- nodescraper/interfaces/connectionmanager.py +143 -0
- nodescraper/interfaces/dataanalyzertask.py +138 -0
- nodescraper/interfaces/datacollectortask.py +185 -0
- nodescraper/interfaces/dataplugin.py +356 -0
- nodescraper/interfaces/plugin.py +127 -0
- nodescraper/interfaces/resultcollator.py +56 -0
- nodescraper/interfaces/task.py +164 -0
- nodescraper/interfaces/taskresulthook.py +39 -0
- nodescraper/models/__init__.py +48 -0
- nodescraper/models/analyzerargs.py +93 -0
- nodescraper/models/collectorargs.py +30 -0
- nodescraper/models/connectionconfig.py +34 -0
- nodescraper/models/datamodel.py +171 -0
- nodescraper/models/datapluginresult.py +39 -0
- nodescraper/models/event.py +158 -0
- nodescraper/models/pluginconfig.py +38 -0
- nodescraper/models/pluginresult.py +39 -0
- nodescraper/models/systeminfo.py +44 -0
- nodescraper/models/taskresult.py +185 -0
- nodescraper/models/timerangeargs.py +38 -0
- nodescraper/pluginexecutor.py +274 -0
- nodescraper/pluginregistry.py +152 -0
- nodescraper/plugins/__init__.py +25 -0
- nodescraper/plugins/inband/__init__.py +25 -0
- nodescraper/plugins/inband/amdsmi/__init__.py +28 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_analyzer.py +821 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_collector.py +1313 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_plugin.py +43 -0
- nodescraper/plugins/inband/amdsmi/amdsmidata.py +1002 -0
- nodescraper/plugins/inband/amdsmi/analyzer_args.py +50 -0
- nodescraper/plugins/inband/amdsmi/cper.py +65 -0
- nodescraper/plugins/inband/bios/__init__.py +29 -0
- nodescraper/plugins/inband/bios/analyzer_args.py +64 -0
- nodescraper/plugins/inband/bios/bios_analyzer.py +93 -0
- nodescraper/plugins/inband/bios/bios_collector.py +93 -0
- nodescraper/plugins/inband/bios/bios_plugin.py +43 -0
- nodescraper/plugins/inband/bios/biosdata.py +30 -0
- nodescraper/plugins/inband/cmdline/__init__.py +25 -0
- nodescraper/plugins/inband/cmdline/analyzer_args.py +80 -0
- nodescraper/plugins/inband/cmdline/cmdline_analyzer.py +113 -0
- nodescraper/plugins/inband/cmdline/cmdline_collector.py +77 -0
- nodescraper/plugins/inband/cmdline/cmdline_plugin.py +43 -0
- nodescraper/plugins/inband/cmdline/cmdlinedata.py +30 -0
- nodescraper/plugins/inband/device_enumeration/__init__.py +29 -0
- nodescraper/plugins/inband/device_enumeration/analyzer_args.py +73 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_analyzer.py +81 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_collector.py +176 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_plugin.py +45 -0
- nodescraper/plugins/inband/device_enumeration/deviceenumdata.py +36 -0
- nodescraper/plugins/inband/dimm/__init__.py +25 -0
- nodescraper/plugins/inband/dimm/collector_args.py +31 -0
- nodescraper/plugins/inband/dimm/dimm_collector.py +151 -0
- nodescraper/plugins/inband/dimm/dimm_plugin.py +40 -0
- nodescraper/plugins/inband/dimm/dimmdata.py +30 -0
- nodescraper/plugins/inband/dkms/__init__.py +25 -0
- nodescraper/plugins/inband/dkms/analyzer_args.py +85 -0
- nodescraper/plugins/inband/dkms/dkms_analyzer.py +106 -0
- nodescraper/plugins/inband/dkms/dkms_collector.py +76 -0
- nodescraper/plugins/inband/dkms/dkms_plugin.py +43 -0
- nodescraper/plugins/inband/dkms/dkmsdata.py +33 -0
- nodescraper/plugins/inband/dmesg/__init__.py +28 -0
- nodescraper/plugins/inband/dmesg/analyzer_args.py +33 -0
- nodescraper/plugins/inband/dmesg/collector_args.py +39 -0
- nodescraper/plugins/inband/dmesg/dmesg_analyzer.py +503 -0
- nodescraper/plugins/inband/dmesg/dmesg_collector.py +164 -0
- nodescraper/plugins/inband/dmesg/dmesg_plugin.py +44 -0
- nodescraper/plugins/inband/dmesg/dmesgdata.py +116 -0
- nodescraper/plugins/inband/fabrics/__init__.py +28 -0
- nodescraper/plugins/inband/fabrics/fabrics_collector.py +726 -0
- nodescraper/plugins/inband/fabrics/fabrics_plugin.py +37 -0
- nodescraper/plugins/inband/fabrics/fabricsdata.py +140 -0
- nodescraper/plugins/inband/journal/__init__.py +28 -0
- nodescraper/plugins/inband/journal/collector_args.py +33 -0
- nodescraper/plugins/inband/journal/journal_collector.py +107 -0
- nodescraper/plugins/inband/journal/journal_plugin.py +40 -0
- nodescraper/plugins/inband/journal/journaldata.py +44 -0
- nodescraper/plugins/inband/kernel/__init__.py +25 -0
- nodescraper/plugins/inband/kernel/analyzer_args.py +64 -0
- nodescraper/plugins/inband/kernel/kernel_analyzer.py +91 -0
- nodescraper/plugins/inband/kernel/kernel_collector.py +129 -0
- nodescraper/plugins/inband/kernel/kernel_plugin.py +43 -0
- nodescraper/plugins/inband/kernel/kerneldata.py +32 -0
- nodescraper/plugins/inband/kernel_module/__init__.py +25 -0
- nodescraper/plugins/inband/kernel_module/analyzer_args.py +59 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_analyzer.py +211 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_collector.py +264 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_data.py +60 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_plugin.py +43 -0
- nodescraper/plugins/inband/memory/__init__.py +25 -0
- nodescraper/plugins/inband/memory/analyzer_args.py +45 -0
- nodescraper/plugins/inband/memory/memory_analyzer.py +98 -0
- nodescraper/plugins/inband/memory/memory_collector.py +330 -0
- nodescraper/plugins/inband/memory/memory_plugin.py +43 -0
- nodescraper/plugins/inband/memory/memorydata.py +90 -0
- nodescraper/plugins/inband/network/__init__.py +28 -0
- nodescraper/plugins/inband/network/network_collector.py +1828 -0
- nodescraper/plugins/inband/network/network_plugin.py +37 -0
- nodescraper/plugins/inband/network/networkdata.py +319 -0
- nodescraper/plugins/inband/nvme/__init__.py +28 -0
- nodescraper/plugins/inband/nvme/nvme_collector.py +167 -0
- nodescraper/plugins/inband/nvme/nvme_plugin.py +37 -0
- nodescraper/plugins/inband/nvme/nvmedata.py +45 -0
- nodescraper/plugins/inband/os/__init__.py +25 -0
- nodescraper/plugins/inband/os/analyzer_args.py +64 -0
- nodescraper/plugins/inband/os/os_analyzer.py +73 -0
- nodescraper/plugins/inband/os/os_collector.py +131 -0
- nodescraper/plugins/inband/os/os_plugin.py +43 -0
- nodescraper/plugins/inband/os/osdata.py +31 -0
- nodescraper/plugins/inband/package/__init__.py +25 -0
- nodescraper/plugins/inband/package/analyzer_args.py +48 -0
- nodescraper/plugins/inband/package/package_analyzer.py +253 -0
- nodescraper/plugins/inband/package/package_collector.py +273 -0
- nodescraper/plugins/inband/package/package_plugin.py +43 -0
- nodescraper/plugins/inband/package/packagedata.py +41 -0
- nodescraper/plugins/inband/pcie/__init__.py +29 -0
- nodescraper/plugins/inband/pcie/analyzer_args.py +63 -0
- nodescraper/plugins/inband/pcie/pcie_analyzer.py +1081 -0
- nodescraper/plugins/inband/pcie/pcie_collector.py +690 -0
- nodescraper/plugins/inband/pcie/pcie_data.py +2017 -0
- nodescraper/plugins/inband/pcie/pcie_plugin.py +43 -0
- nodescraper/plugins/inband/process/__init__.py +25 -0
- nodescraper/plugins/inband/process/analyzer_args.py +45 -0
- nodescraper/plugins/inband/process/collector_args.py +31 -0
- nodescraper/plugins/inband/process/process_analyzer.py +91 -0
- nodescraper/plugins/inband/process/process_collector.py +115 -0
- nodescraper/plugins/inband/process/process_plugin.py +46 -0
- nodescraper/plugins/inband/process/processdata.py +34 -0
- nodescraper/plugins/inband/rocm/__init__.py +25 -0
- nodescraper/plugins/inband/rocm/analyzer_args.py +66 -0
- nodescraper/plugins/inband/rocm/rocm_analyzer.py +100 -0
- nodescraper/plugins/inband/rocm/rocm_collector.py +205 -0
- nodescraper/plugins/inband/rocm/rocm_plugin.py +43 -0
- nodescraper/plugins/inband/rocm/rocmdata.py +62 -0
- nodescraper/plugins/inband/storage/__init__.py +25 -0
- nodescraper/plugins/inband/storage/analyzer_args.py +38 -0
- nodescraper/plugins/inband/storage/collector_args.py +31 -0
- nodescraper/plugins/inband/storage/storage_analyzer.py +152 -0
- nodescraper/plugins/inband/storage/storage_collector.py +110 -0
- nodescraper/plugins/inband/storage/storage_plugin.py +44 -0
- nodescraper/plugins/inband/storage/storagedata.py +70 -0
- nodescraper/plugins/inband/sysctl/__init__.py +29 -0
- nodescraper/plugins/inband/sysctl/analyzer_args.py +67 -0
- nodescraper/plugins/inband/sysctl/sysctl_analyzer.py +81 -0
- nodescraper/plugins/inband/sysctl/sysctl_collector.py +101 -0
- nodescraper/plugins/inband/sysctl/sysctl_plugin.py +43 -0
- nodescraper/plugins/inband/sysctl/sysctldata.py +42 -0
- nodescraper/plugins/inband/syslog/__init__.py +28 -0
- nodescraper/plugins/inband/syslog/syslog_collector.py +121 -0
- nodescraper/plugins/inband/syslog/syslog_plugin.py +37 -0
- nodescraper/plugins/inband/syslog/syslogdata.py +46 -0
- nodescraper/plugins/inband/uptime/__init__.py +25 -0
- nodescraper/plugins/inband/uptime/uptime_collector.py +88 -0
- nodescraper/plugins/inband/uptime/uptime_plugin.py +37 -0
- nodescraper/plugins/inband/uptime/uptimedata.py +31 -0
- nodescraper/resultcollators/__init__.py +25 -0
- nodescraper/resultcollators/tablesummary.py +159 -0
- nodescraper/taskresulthooks/__init__.py +28 -0
- nodescraper/taskresulthooks/filesystemloghook.py +88 -0
- nodescraper/typeutils.py +171 -0
- nodescraper/utils.py +412 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
#
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
# copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
# SOFTWARE.
|
|
24
|
+
#
|
|
25
|
+
###############################################################################
|
|
26
|
+
import logging
|
|
27
|
+
from typing import Generic, Optional, Type, Union
|
|
28
|
+
|
|
29
|
+
from nodescraper.enums import EventPriority, ExecutionStatus, SystemInteractionLevel
|
|
30
|
+
from nodescraper.generictypes import TAnalyzeArg, TCollectArg, TDataModel
|
|
31
|
+
from nodescraper.interfaces.dataanalyzertask import DataAnalyzer
|
|
32
|
+
from nodescraper.interfaces.datacollectortask import DataCollector
|
|
33
|
+
from nodescraper.interfaces.plugin import PluginInterface
|
|
34
|
+
from nodescraper.models import (
|
|
35
|
+
AnalyzerArgs,
|
|
36
|
+
DataPluginResult,
|
|
37
|
+
PluginResult,
|
|
38
|
+
SystemInfo,
|
|
39
|
+
TaskResult,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
from .connectionmanager import TConnectArg, TConnectionManager
|
|
43
|
+
from .task import SystemCompatibilityError
|
|
44
|
+
from .taskresulthook import TaskResultHook
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DataPlugin(
|
|
48
|
+
PluginInterface, Generic[TConnectionManager, TConnectArg, TDataModel, TCollectArg, TAnalyzeArg]
|
|
49
|
+
):
|
|
50
|
+
"""Plugin used to collect and analyze data"""
|
|
51
|
+
|
|
52
|
+
DATA_MODEL: Type[TDataModel]
|
|
53
|
+
|
|
54
|
+
CONNECTION_TYPE: Optional[Type[TConnectionManager]]
|
|
55
|
+
|
|
56
|
+
COLLECTOR: Optional[Type[DataCollector]] = None
|
|
57
|
+
|
|
58
|
+
ANALYZER: Optional[Type[DataAnalyzer]] = None
|
|
59
|
+
|
|
60
|
+
ANALYZER_ARGS: Optional[Type[AnalyzerArgs]] = None
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
system_info: SystemInfo,
|
|
65
|
+
logger: Optional[logging.Logger] = None,
|
|
66
|
+
connection_manager: Optional[TConnectionManager] = None,
|
|
67
|
+
connection_args: Optional[Union[TConnectArg, dict]] = None,
|
|
68
|
+
task_result_hooks: Optional[list[TaskResultHook]] = None,
|
|
69
|
+
log_path: Optional[str] = None,
|
|
70
|
+
**kwargs,
|
|
71
|
+
):
|
|
72
|
+
super().__init__(
|
|
73
|
+
system_info,
|
|
74
|
+
logger,
|
|
75
|
+
connection_manager,
|
|
76
|
+
connection_args,
|
|
77
|
+
task_result_hooks,
|
|
78
|
+
log_path,
|
|
79
|
+
**kwargs,
|
|
80
|
+
)
|
|
81
|
+
self._validate_class_var()
|
|
82
|
+
self.collection_result: TaskResult = TaskResult(
|
|
83
|
+
status=ExecutionStatus.NOT_RAN,
|
|
84
|
+
message=f"Data collection not ran for {self.__class__.__name__}",
|
|
85
|
+
)
|
|
86
|
+
self.analysis_result: TaskResult = TaskResult(
|
|
87
|
+
status=ExecutionStatus.NOT_RAN,
|
|
88
|
+
message=f"Data analysis not ran for {self.__class__.__name__}",
|
|
89
|
+
)
|
|
90
|
+
self._data: Optional[TDataModel] = None
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def _validate_class_var(cls):
|
|
94
|
+
if not hasattr(cls, "DATA_MODEL"):
|
|
95
|
+
raise TypeError(f"No data model set for {cls.__name__}")
|
|
96
|
+
|
|
97
|
+
if cls.DATA_MODEL is None:
|
|
98
|
+
raise TypeError("DATA_MODEL class variable not defined")
|
|
99
|
+
|
|
100
|
+
if not cls.COLLECTOR and not cls.ANALYZER:
|
|
101
|
+
raise TypeError("No collector or analyzer task defined")
|
|
102
|
+
|
|
103
|
+
if cls.COLLECTOR and not cls.CONNECTION_TYPE:
|
|
104
|
+
raise TypeError("CONNECTION_TYPE must be defined for collector")
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def is_valid(cls) -> bool:
|
|
108
|
+
"""Check that all required class variables are set
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
bool: bool indicating validity
|
|
112
|
+
"""
|
|
113
|
+
try:
|
|
114
|
+
cls._validate_class_var()
|
|
115
|
+
except TypeError:
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
return super().is_valid()
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def data(self) -> Optional[TDataModel]:
|
|
122
|
+
"""Retrieve data model
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Optional[TDataModel]: data model
|
|
126
|
+
"""
|
|
127
|
+
return self._data
|
|
128
|
+
|
|
129
|
+
@data.setter
|
|
130
|
+
def data(self, data: Optional[Union[str, dict, TDataModel]]):
|
|
131
|
+
if isinstance(data, (str, dict)):
|
|
132
|
+
self._data = self.DATA_MODEL.import_model(data)
|
|
133
|
+
elif not isinstance(data, self.DATA_MODEL):
|
|
134
|
+
raise ValueError(f"data is invalid type, expected {self.DATA_MODEL.__class__.__name__}")
|
|
135
|
+
else:
|
|
136
|
+
self._data = data
|
|
137
|
+
|
|
138
|
+
def collect(
|
|
139
|
+
self,
|
|
140
|
+
max_event_priority_level: Optional[Union[EventPriority, str]] = EventPriority.CRITICAL,
|
|
141
|
+
system_interaction_level: Optional[
|
|
142
|
+
Union[SystemInteractionLevel, str]
|
|
143
|
+
] = SystemInteractionLevel.INTERACTIVE,
|
|
144
|
+
preserve_connection: bool = False,
|
|
145
|
+
collection_args: Optional[Union[TCollectArg, dict]] = None,
|
|
146
|
+
) -> TaskResult:
|
|
147
|
+
"""Run data collector task
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
max_event_priority_level (Union[EventPriority, str], optional): priority limit for events. Defaults to EventPriority.CRITICAL.
|
|
151
|
+
system_interaction_level (Union[SystemInteractionLevel, str], optional): system interaction level. Defaults to SystemInteractionLevel.INTERACTIVE.
|
|
152
|
+
preserve_connection (bool, optional): whether we should close the connection after data collection. Defaults to False.
|
|
153
|
+
collection_args (Optional[Union[TCollectArg , dict]], optional): args for data collection. Defaults to None.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
TaskResult: task result for data collection
|
|
157
|
+
"""
|
|
158
|
+
if not self.COLLECTOR:
|
|
159
|
+
self.collection_result = TaskResult(
|
|
160
|
+
parent=self.__class__.__name__,
|
|
161
|
+
status=ExecutionStatus.NOT_RAN,
|
|
162
|
+
message=f"Data collection not supported for {self.__class__.__name__}",
|
|
163
|
+
)
|
|
164
|
+
return self.collection_result
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
if not self.connection_manager:
|
|
168
|
+
if not self.CONNECTION_TYPE:
|
|
169
|
+
self.collection_result = TaskResult(
|
|
170
|
+
task=self.COLLECTOR.__name__,
|
|
171
|
+
parent=self.__class__.__name__,
|
|
172
|
+
status=ExecutionStatus.NOT_RAN,
|
|
173
|
+
message=f"No connection manager type provided for {self.__class__.__name__}",
|
|
174
|
+
)
|
|
175
|
+
return self.collection_result
|
|
176
|
+
self.logger.info("No connection manager provide, initializing connection manager")
|
|
177
|
+
self.connection_manager = self.CONNECTION_TYPE(
|
|
178
|
+
system_info=self.system_info,
|
|
179
|
+
logger=self.logger,
|
|
180
|
+
parent=self.__class__.__name__,
|
|
181
|
+
task_result_hooks=self.task_result_hooks,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if (
|
|
185
|
+
not self.connection_manager.connection
|
|
186
|
+
and self.connection_manager.result.status == ExecutionStatus.UNSET
|
|
187
|
+
):
|
|
188
|
+
self.connection_manager.connect()
|
|
189
|
+
|
|
190
|
+
if self.connection_manager.result.status != ExecutionStatus.OK:
|
|
191
|
+
self.collection_result = TaskResult(
|
|
192
|
+
task=self.COLLECTOR.__name__,
|
|
193
|
+
parent=self.__class__.__name__,
|
|
194
|
+
status=ExecutionStatus.NOT_RAN,
|
|
195
|
+
message="Connection not available, data collection skipped",
|
|
196
|
+
)
|
|
197
|
+
else:
|
|
198
|
+
|
|
199
|
+
collection_task = self.COLLECTOR(
|
|
200
|
+
system_info=self.system_info,
|
|
201
|
+
logger=self.logger,
|
|
202
|
+
system_interaction_level=system_interaction_level,
|
|
203
|
+
connection=self.connection_manager.connection,
|
|
204
|
+
max_event_priority_level=max_event_priority_level,
|
|
205
|
+
parent=self.__class__.__name__,
|
|
206
|
+
task_result_hooks=self.task_result_hooks,
|
|
207
|
+
)
|
|
208
|
+
self.collection_result, self._data = collection_task.collect_data(collection_args)
|
|
209
|
+
|
|
210
|
+
except SystemCompatibilityError as e:
|
|
211
|
+
self.collection_result = TaskResult(
|
|
212
|
+
task=self.COLLECTOR.__name__,
|
|
213
|
+
parent=self.__class__.__name__,
|
|
214
|
+
status=ExecutionStatus.NOT_RAN,
|
|
215
|
+
message=str(e),
|
|
216
|
+
)
|
|
217
|
+
except Exception as e:
|
|
218
|
+
self.collection_result = TaskResult(
|
|
219
|
+
task=self.COLLECTOR.__name__,
|
|
220
|
+
parent=self.__class__.__name__,
|
|
221
|
+
status=ExecutionStatus.EXECUTION_FAILURE,
|
|
222
|
+
message=f"Unhandled exception running data collector: {str(e)}",
|
|
223
|
+
)
|
|
224
|
+
finally:
|
|
225
|
+
if not preserve_connection and self.connection_manager:
|
|
226
|
+
self.connection_manager.disconnect()
|
|
227
|
+
|
|
228
|
+
return self.collection_result
|
|
229
|
+
|
|
230
|
+
def analyze(
|
|
231
|
+
self,
|
|
232
|
+
max_event_priority_level: Optional[Union[EventPriority, str]] = EventPriority.CRITICAL,
|
|
233
|
+
analysis_args: Optional[Union[TAnalyzeArg, dict]] = None,
|
|
234
|
+
data: Optional[Union[str, dict, TDataModel]] = None,
|
|
235
|
+
) -> TaskResult:
|
|
236
|
+
"""Run data analyzer task
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
max_event_priority_level (Union[EventPriority, str], optional): priority limit for events. Defaults to EventPriority.CRITICAL.
|
|
240
|
+
analysis_args (Optional[Union[TAnalyzeArg , dict]], optional): args for data analysis. Defaults to None.
|
|
241
|
+
data (Optional[Union[str, dict, TDataModel]], optional): data to analyze. Defaults to None.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
TaskResult: result of data analysis
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
if self.ANALYZER is None:
|
|
248
|
+
self.analysis_result = TaskResult(
|
|
249
|
+
status=ExecutionStatus.NOT_RAN,
|
|
250
|
+
parent=self.__class__.__name__,
|
|
251
|
+
message=f"Data analysis not supported for {self.__class__.__name__}",
|
|
252
|
+
)
|
|
253
|
+
return self.analysis_result
|
|
254
|
+
|
|
255
|
+
if data:
|
|
256
|
+
self.data = data
|
|
257
|
+
|
|
258
|
+
if self.data is None:
|
|
259
|
+
self.analysis_result = TaskResult(
|
|
260
|
+
task=self.ANALYZER.__name__,
|
|
261
|
+
parent=self.__class__.__name__,
|
|
262
|
+
status=ExecutionStatus.NOT_RAN,
|
|
263
|
+
message=f"No data available to analyze for {self.__class__.__name__}",
|
|
264
|
+
)
|
|
265
|
+
return self.analysis_result
|
|
266
|
+
|
|
267
|
+
analyzer_task = self.ANALYZER(
|
|
268
|
+
self.system_info,
|
|
269
|
+
logger=self.logger,
|
|
270
|
+
max_event_priority_level=max_event_priority_level,
|
|
271
|
+
parent=self.__class__.__name__,
|
|
272
|
+
task_result_hooks=self.task_result_hooks,
|
|
273
|
+
)
|
|
274
|
+
self.analysis_result = analyzer_task.analyze_data(self.data, analysis_args)
|
|
275
|
+
return self.analysis_result
|
|
276
|
+
|
|
277
|
+
def run(
|
|
278
|
+
self,
|
|
279
|
+
collection: bool = True,
|
|
280
|
+
analysis: bool = True,
|
|
281
|
+
max_event_priority_level: Union[EventPriority, str] = EventPriority.CRITICAL,
|
|
282
|
+
system_interaction_level: Union[
|
|
283
|
+
SystemInteractionLevel, str
|
|
284
|
+
] = SystemInteractionLevel.INTERACTIVE,
|
|
285
|
+
preserve_connection: bool = False,
|
|
286
|
+
data: Optional[Union[str, dict, TDataModel]] = None,
|
|
287
|
+
collection_args: Optional[Union[TCollectArg, dict]] = None,
|
|
288
|
+
analysis_args: Optional[Union[TAnalyzeArg, dict]] = None,
|
|
289
|
+
) -> PluginResult:
|
|
290
|
+
"""Run plugin
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
collection (bool, optional): Enable data collection. Defaults to True.
|
|
294
|
+
analysis (bool, optional): Enable data analysis. Defaults to True.
|
|
295
|
+
max_event_priority_level (Union[EventPriority, str], optional): Max priority level to assign to events. Defaults to EventPriority.CRITICAL.
|
|
296
|
+
system_interaction_level (Union[SystemInteractionLevel, str], optional): System interaction level. Defaults to SystemInteractionLevel.INTERACTIVE.
|
|
297
|
+
preserve_connection (bool, optional): Whether to close the connection when data collection is complete. Defaults to False.
|
|
298
|
+
data (Optional[Union[str, dict, TDataModel]], optional): Input data. Defaults to None.
|
|
299
|
+
collection_args (Optional[Union[TCollectArg , dict]], optional): Arguments for data collection. Defaults to None.
|
|
300
|
+
analysis_args (Optional[Union[TAnalyzeArg , dict]], optional): Arguments for data analysis. Defaults to None.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
PluginResult: Plugin result
|
|
304
|
+
"""
|
|
305
|
+
self.logger.info("Running plugin %s", self.__class__.__name__)
|
|
306
|
+
if collection:
|
|
307
|
+
self.collect(
|
|
308
|
+
max_event_priority_level=max_event_priority_level,
|
|
309
|
+
system_interaction_level=system_interaction_level,
|
|
310
|
+
collection_args=collection_args,
|
|
311
|
+
preserve_connection=preserve_connection,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
if analysis:
|
|
315
|
+
self.analyze(
|
|
316
|
+
max_event_priority_level=max_event_priority_level,
|
|
317
|
+
analysis_args=analysis_args,
|
|
318
|
+
data=data,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
status = max(self.collection_result.status, self.analysis_result.status)
|
|
322
|
+
|
|
323
|
+
message = ""
|
|
324
|
+
if status == ExecutionStatus.NOT_RAN:
|
|
325
|
+
message = "Plugin tasks not ran"
|
|
326
|
+
elif status in [
|
|
327
|
+
ExecutionStatus.ERROR,
|
|
328
|
+
ExecutionStatus.EXECUTION_FAILURE,
|
|
329
|
+
ExecutionStatus.WARNING,
|
|
330
|
+
]:
|
|
331
|
+
if self.analysis_result.status > self.collection_result.status:
|
|
332
|
+
message = (
|
|
333
|
+
f"Analysis warning: {self.analysis_result.message}"
|
|
334
|
+
if self.analysis_result.status == ExecutionStatus.WARNING
|
|
335
|
+
else f"Analysis error: {self.analysis_result.message}"
|
|
336
|
+
)
|
|
337
|
+
else:
|
|
338
|
+
|
|
339
|
+
message = (
|
|
340
|
+
f"Collection warning: {self.collection_result.message}"
|
|
341
|
+
if self.collection_result.status == ExecutionStatus.WARNING
|
|
342
|
+
else f"Collection error: {self.collection_result.message}"
|
|
343
|
+
)
|
|
344
|
+
else:
|
|
345
|
+
message = "Plugin tasks completed successfully"
|
|
346
|
+
|
|
347
|
+
return PluginResult(
|
|
348
|
+
status=max(self.collection_result.status, self.analysis_result.status),
|
|
349
|
+
source=self.__class__.__name__,
|
|
350
|
+
message=message,
|
|
351
|
+
result_data=DataPluginResult(
|
|
352
|
+
system_data=self.data,
|
|
353
|
+
collection_result=self.collection_result,
|
|
354
|
+
analysis_result=self.analysis_result,
|
|
355
|
+
),
|
|
356
|
+
)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
#
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
# copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
# SOFTWARE.
|
|
24
|
+
#
|
|
25
|
+
###############################################################################
|
|
26
|
+
import abc
|
|
27
|
+
import inspect
|
|
28
|
+
import logging
|
|
29
|
+
from typing import Callable, Generic, Optional, Type, Union
|
|
30
|
+
|
|
31
|
+
from nodescraper.constants import DEFAULT_LOGGER
|
|
32
|
+
from nodescraper.models import PluginResult, SystemInfo
|
|
33
|
+
from nodescraper.taskresulthooks.filesystemloghook import FileSystemLogHook
|
|
34
|
+
|
|
35
|
+
from .connectionmanager import TConnectArg, TConnectionManager
|
|
36
|
+
from .taskresulthook import TaskResultHook
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class PluginInterface(abc.ABC, Generic[TConnectionManager, TConnectArg]):
|
|
40
|
+
"""Base plugin interface"""
|
|
41
|
+
|
|
42
|
+
CONNECTION_TYPE: Optional[Type[TConnectionManager]] = None
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
system_info: Optional[SystemInfo] = None,
|
|
47
|
+
logger: Optional[logging.Logger] = None,
|
|
48
|
+
connection_manager: Optional[TConnectionManager] = None,
|
|
49
|
+
connection_args: Optional[Union[TConnectArg, dict]] = None,
|
|
50
|
+
task_result_hooks: Optional[list[TaskResultHook]] = None,
|
|
51
|
+
log_path: Optional[str] = None,
|
|
52
|
+
queue_callback: Optional[Callable] = None,
|
|
53
|
+
**kwargs,
|
|
54
|
+
):
|
|
55
|
+
"""Initialize plugin
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
system_info (Optional[SystemInfo], optional): system info object. Defaults to None.
|
|
59
|
+
logger (Optional[logging.Logger], optional): python logger instance. Defaults to None.
|
|
60
|
+
connection_manager (Optional[TConnectionManager], optional): connection manager instance. Defaults to None.
|
|
61
|
+
connection_args (Optional[Union[TConnectArg , dict]], optional): connection args. Defaults to None.
|
|
62
|
+
task_result_hooks (Optional[list[TaskResultHook]], optional): list of task result hooks. Defaults to None.
|
|
63
|
+
log_path (Optional[str], optional): path for file system logs. Defaults to None.
|
|
64
|
+
queue_callback (Optional[Callable], optional): function to add additional plugins to plugin executor queue. Defaults to None.
|
|
65
|
+
"""
|
|
66
|
+
if logger is None:
|
|
67
|
+
logger = logging.getLogger(DEFAULT_LOGGER)
|
|
68
|
+
self.logger = logger
|
|
69
|
+
|
|
70
|
+
if system_info is None:
|
|
71
|
+
system_info = SystemInfo()
|
|
72
|
+
self.system_info = system_info
|
|
73
|
+
|
|
74
|
+
if not task_result_hooks:
|
|
75
|
+
task_result_hooks = []
|
|
76
|
+
self.task_result_hooks = task_result_hooks
|
|
77
|
+
|
|
78
|
+
if log_path:
|
|
79
|
+
for hook in self.task_result_hooks:
|
|
80
|
+
if isinstance(hook, FileSystemLogHook):
|
|
81
|
+
break
|
|
82
|
+
else:
|
|
83
|
+
self.task_result_hooks.append(FileSystemLogHook(log_base_path=log_path))
|
|
84
|
+
|
|
85
|
+
self.log_path = log_path
|
|
86
|
+
|
|
87
|
+
self.queue_callback = queue_callback
|
|
88
|
+
|
|
89
|
+
self.connection_manager = connection_manager
|
|
90
|
+
|
|
91
|
+
if connection_args and self.CONNECTION_TYPE and not self.connection_manager:
|
|
92
|
+
self.connection_manager = self.CONNECTION_TYPE(
|
|
93
|
+
system_info=self.system_info,
|
|
94
|
+
logger=logger,
|
|
95
|
+
connection_args=connection_args,
|
|
96
|
+
parent=self.__class__.__name__,
|
|
97
|
+
task_result_hooks=self.task_result_hooks,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def is_valid(cls) -> bool:
|
|
102
|
+
"""Check if plugin class is valid and can be instantiated
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
bool: class validity
|
|
106
|
+
"""
|
|
107
|
+
if inspect.isabstract(cls):
|
|
108
|
+
return False
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
def _update_queue(self, queue_item: tuple) -> None:
|
|
112
|
+
"""call queue_callback to add additional item to plugin queue if run using plugin executor
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
queue_item (dict): plugin config item to add to queue
|
|
116
|
+
"""
|
|
117
|
+
if self.queue_callback:
|
|
118
|
+
self.queue_callback(queue_item)
|
|
119
|
+
|
|
120
|
+
@abc.abstractmethod
|
|
121
|
+
def run(self, **kwargs) -> PluginResult:
|
|
122
|
+
"""Plugin run function
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
PluginResult: plugin result object
|
|
126
|
+
"""
|
|
127
|
+
pass
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
#
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
# copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
# SOFTWARE.
|
|
24
|
+
#
|
|
25
|
+
###############################################################################
|
|
26
|
+
import abc
|
|
27
|
+
import logging
|
|
28
|
+
from typing import Optional
|
|
29
|
+
|
|
30
|
+
from nodescraper.constants import DEFAULT_LOGGER
|
|
31
|
+
from nodescraper.models import PluginResult, TaskResult
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PluginResultCollator(abc.ABC):
|
|
35
|
+
"""Base interface for plugin result collators"""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
logger: Optional[logging.Logger] = None,
|
|
40
|
+
log_path: Optional[str] = None,
|
|
41
|
+
):
|
|
42
|
+
if logger is None:
|
|
43
|
+
logger = logging.getLogger(DEFAULT_LOGGER)
|
|
44
|
+
self.logger = logger
|
|
45
|
+
self.log_path = log_path
|
|
46
|
+
|
|
47
|
+
@abc.abstractmethod
|
|
48
|
+
def collate_results(
|
|
49
|
+
self, plugin_results: list[PluginResult], connection_results: list[TaskResult], **kwargs
|
|
50
|
+
):
|
|
51
|
+
"""Function to process the result of a plugin executor run
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
plugin_results (list[PluginResult]): list of plugin result objects
|
|
55
|
+
connection_results (list[TaskResult]): list of task result objests from connection setup
|
|
56
|
+
"""
|