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.
Files changed (197) hide show
  1. amd_node_scraper-0.0.1.dist-info/LICENSE +21 -0
  2. amd_node_scraper-0.0.1.dist-info/METADATA +424 -0
  3. amd_node_scraper-0.0.1.dist-info/RECORD +197 -0
  4. amd_node_scraper-0.0.1.dist-info/WHEEL +5 -0
  5. amd_node_scraper-0.0.1.dist-info/entry_points.txt +2 -0
  6. amd_node_scraper-0.0.1.dist-info/top_level.txt +1 -0
  7. nodescraper/__init__.py +32 -0
  8. nodescraper/base/__init__.py +34 -0
  9. nodescraper/base/inbandcollectortask.py +118 -0
  10. nodescraper/base/inbanddataplugin.py +39 -0
  11. nodescraper/base/regexanalyzer.py +120 -0
  12. nodescraper/cli/__init__.py +29 -0
  13. nodescraper/cli/cli.py +511 -0
  14. nodescraper/cli/constants.py +27 -0
  15. nodescraper/cli/dynamicparserbuilder.py +171 -0
  16. nodescraper/cli/helper.py +517 -0
  17. nodescraper/cli/inputargtypes.py +129 -0
  18. nodescraper/configbuilder.py +123 -0
  19. nodescraper/configregistry.py +66 -0
  20. nodescraper/configs/node_status.json +19 -0
  21. nodescraper/connection/__init__.py +25 -0
  22. nodescraper/connection/inband/__init__.py +46 -0
  23. nodescraper/connection/inband/inband.py +171 -0
  24. nodescraper/connection/inband/inbandlocal.py +93 -0
  25. nodescraper/connection/inband/inbandmanager.py +151 -0
  26. nodescraper/connection/inband/inbandremote.py +173 -0
  27. nodescraper/connection/inband/sshparams.py +43 -0
  28. nodescraper/constants.py +26 -0
  29. nodescraper/enums/__init__.py +40 -0
  30. nodescraper/enums/eventcategory.py +89 -0
  31. nodescraper/enums/eventpriority.py +42 -0
  32. nodescraper/enums/executionstatus.py +44 -0
  33. nodescraper/enums/osfamily.py +34 -0
  34. nodescraper/enums/systeminteraction.py +41 -0
  35. nodescraper/enums/systemlocation.py +33 -0
  36. nodescraper/generictypes.py +36 -0
  37. nodescraper/interfaces/__init__.py +44 -0
  38. nodescraper/interfaces/connectionmanager.py +143 -0
  39. nodescraper/interfaces/dataanalyzertask.py +138 -0
  40. nodescraper/interfaces/datacollectortask.py +185 -0
  41. nodescraper/interfaces/dataplugin.py +356 -0
  42. nodescraper/interfaces/plugin.py +127 -0
  43. nodescraper/interfaces/resultcollator.py +56 -0
  44. nodescraper/interfaces/task.py +164 -0
  45. nodescraper/interfaces/taskresulthook.py +39 -0
  46. nodescraper/models/__init__.py +48 -0
  47. nodescraper/models/analyzerargs.py +93 -0
  48. nodescraper/models/collectorargs.py +30 -0
  49. nodescraper/models/connectionconfig.py +34 -0
  50. nodescraper/models/datamodel.py +171 -0
  51. nodescraper/models/datapluginresult.py +39 -0
  52. nodescraper/models/event.py +158 -0
  53. nodescraper/models/pluginconfig.py +38 -0
  54. nodescraper/models/pluginresult.py +39 -0
  55. nodescraper/models/systeminfo.py +44 -0
  56. nodescraper/models/taskresult.py +185 -0
  57. nodescraper/models/timerangeargs.py +38 -0
  58. nodescraper/pluginexecutor.py +274 -0
  59. nodescraper/pluginregistry.py +152 -0
  60. nodescraper/plugins/__init__.py +25 -0
  61. nodescraper/plugins/inband/__init__.py +25 -0
  62. nodescraper/plugins/inband/amdsmi/__init__.py +28 -0
  63. nodescraper/plugins/inband/amdsmi/amdsmi_analyzer.py +821 -0
  64. nodescraper/plugins/inband/amdsmi/amdsmi_collector.py +1313 -0
  65. nodescraper/plugins/inband/amdsmi/amdsmi_plugin.py +43 -0
  66. nodescraper/plugins/inband/amdsmi/amdsmidata.py +1002 -0
  67. nodescraper/plugins/inband/amdsmi/analyzer_args.py +50 -0
  68. nodescraper/plugins/inband/amdsmi/cper.py +65 -0
  69. nodescraper/plugins/inband/bios/__init__.py +29 -0
  70. nodescraper/plugins/inband/bios/analyzer_args.py +64 -0
  71. nodescraper/plugins/inband/bios/bios_analyzer.py +93 -0
  72. nodescraper/plugins/inband/bios/bios_collector.py +93 -0
  73. nodescraper/plugins/inband/bios/bios_plugin.py +43 -0
  74. nodescraper/plugins/inband/bios/biosdata.py +30 -0
  75. nodescraper/plugins/inband/cmdline/__init__.py +25 -0
  76. nodescraper/plugins/inband/cmdline/analyzer_args.py +80 -0
  77. nodescraper/plugins/inband/cmdline/cmdline_analyzer.py +113 -0
  78. nodescraper/plugins/inband/cmdline/cmdline_collector.py +77 -0
  79. nodescraper/plugins/inband/cmdline/cmdline_plugin.py +43 -0
  80. nodescraper/plugins/inband/cmdline/cmdlinedata.py +30 -0
  81. nodescraper/plugins/inband/device_enumeration/__init__.py +29 -0
  82. nodescraper/plugins/inband/device_enumeration/analyzer_args.py +73 -0
  83. nodescraper/plugins/inband/device_enumeration/device_enumeration_analyzer.py +81 -0
  84. nodescraper/plugins/inband/device_enumeration/device_enumeration_collector.py +176 -0
  85. nodescraper/plugins/inband/device_enumeration/device_enumeration_plugin.py +45 -0
  86. nodescraper/plugins/inband/device_enumeration/deviceenumdata.py +36 -0
  87. nodescraper/plugins/inband/dimm/__init__.py +25 -0
  88. nodescraper/plugins/inband/dimm/collector_args.py +31 -0
  89. nodescraper/plugins/inband/dimm/dimm_collector.py +151 -0
  90. nodescraper/plugins/inband/dimm/dimm_plugin.py +40 -0
  91. nodescraper/plugins/inband/dimm/dimmdata.py +30 -0
  92. nodescraper/plugins/inband/dkms/__init__.py +25 -0
  93. nodescraper/plugins/inband/dkms/analyzer_args.py +85 -0
  94. nodescraper/plugins/inband/dkms/dkms_analyzer.py +106 -0
  95. nodescraper/plugins/inband/dkms/dkms_collector.py +76 -0
  96. nodescraper/plugins/inband/dkms/dkms_plugin.py +43 -0
  97. nodescraper/plugins/inband/dkms/dkmsdata.py +33 -0
  98. nodescraper/plugins/inband/dmesg/__init__.py +28 -0
  99. nodescraper/plugins/inband/dmesg/analyzer_args.py +33 -0
  100. nodescraper/plugins/inband/dmesg/collector_args.py +39 -0
  101. nodescraper/plugins/inband/dmesg/dmesg_analyzer.py +503 -0
  102. nodescraper/plugins/inband/dmesg/dmesg_collector.py +164 -0
  103. nodescraper/plugins/inband/dmesg/dmesg_plugin.py +44 -0
  104. nodescraper/plugins/inband/dmesg/dmesgdata.py +116 -0
  105. nodescraper/plugins/inband/fabrics/__init__.py +28 -0
  106. nodescraper/plugins/inband/fabrics/fabrics_collector.py +726 -0
  107. nodescraper/plugins/inband/fabrics/fabrics_plugin.py +37 -0
  108. nodescraper/plugins/inband/fabrics/fabricsdata.py +140 -0
  109. nodescraper/plugins/inband/journal/__init__.py +28 -0
  110. nodescraper/plugins/inband/journal/collector_args.py +33 -0
  111. nodescraper/plugins/inband/journal/journal_collector.py +107 -0
  112. nodescraper/plugins/inband/journal/journal_plugin.py +40 -0
  113. nodescraper/plugins/inband/journal/journaldata.py +44 -0
  114. nodescraper/plugins/inband/kernel/__init__.py +25 -0
  115. nodescraper/plugins/inband/kernel/analyzer_args.py +64 -0
  116. nodescraper/plugins/inband/kernel/kernel_analyzer.py +91 -0
  117. nodescraper/plugins/inband/kernel/kernel_collector.py +129 -0
  118. nodescraper/plugins/inband/kernel/kernel_plugin.py +43 -0
  119. nodescraper/plugins/inband/kernel/kerneldata.py +32 -0
  120. nodescraper/plugins/inband/kernel_module/__init__.py +25 -0
  121. nodescraper/plugins/inband/kernel_module/analyzer_args.py +59 -0
  122. nodescraper/plugins/inband/kernel_module/kernel_module_analyzer.py +211 -0
  123. nodescraper/plugins/inband/kernel_module/kernel_module_collector.py +264 -0
  124. nodescraper/plugins/inband/kernel_module/kernel_module_data.py +60 -0
  125. nodescraper/plugins/inband/kernel_module/kernel_module_plugin.py +43 -0
  126. nodescraper/plugins/inband/memory/__init__.py +25 -0
  127. nodescraper/plugins/inband/memory/analyzer_args.py +45 -0
  128. nodescraper/plugins/inband/memory/memory_analyzer.py +98 -0
  129. nodescraper/plugins/inband/memory/memory_collector.py +330 -0
  130. nodescraper/plugins/inband/memory/memory_plugin.py +43 -0
  131. nodescraper/plugins/inband/memory/memorydata.py +90 -0
  132. nodescraper/plugins/inband/network/__init__.py +28 -0
  133. nodescraper/plugins/inband/network/network_collector.py +1828 -0
  134. nodescraper/plugins/inband/network/network_plugin.py +37 -0
  135. nodescraper/plugins/inband/network/networkdata.py +319 -0
  136. nodescraper/plugins/inband/nvme/__init__.py +28 -0
  137. nodescraper/plugins/inband/nvme/nvme_collector.py +167 -0
  138. nodescraper/plugins/inband/nvme/nvme_plugin.py +37 -0
  139. nodescraper/plugins/inband/nvme/nvmedata.py +45 -0
  140. nodescraper/plugins/inband/os/__init__.py +25 -0
  141. nodescraper/plugins/inband/os/analyzer_args.py +64 -0
  142. nodescraper/plugins/inband/os/os_analyzer.py +73 -0
  143. nodescraper/plugins/inband/os/os_collector.py +131 -0
  144. nodescraper/plugins/inband/os/os_plugin.py +43 -0
  145. nodescraper/plugins/inband/os/osdata.py +31 -0
  146. nodescraper/plugins/inband/package/__init__.py +25 -0
  147. nodescraper/plugins/inband/package/analyzer_args.py +48 -0
  148. nodescraper/plugins/inband/package/package_analyzer.py +253 -0
  149. nodescraper/plugins/inband/package/package_collector.py +273 -0
  150. nodescraper/plugins/inband/package/package_plugin.py +43 -0
  151. nodescraper/plugins/inband/package/packagedata.py +41 -0
  152. nodescraper/plugins/inband/pcie/__init__.py +29 -0
  153. nodescraper/plugins/inband/pcie/analyzer_args.py +63 -0
  154. nodescraper/plugins/inband/pcie/pcie_analyzer.py +1081 -0
  155. nodescraper/plugins/inband/pcie/pcie_collector.py +690 -0
  156. nodescraper/plugins/inband/pcie/pcie_data.py +2017 -0
  157. nodescraper/plugins/inband/pcie/pcie_plugin.py +43 -0
  158. nodescraper/plugins/inband/process/__init__.py +25 -0
  159. nodescraper/plugins/inband/process/analyzer_args.py +45 -0
  160. nodescraper/plugins/inband/process/collector_args.py +31 -0
  161. nodescraper/plugins/inband/process/process_analyzer.py +91 -0
  162. nodescraper/plugins/inband/process/process_collector.py +115 -0
  163. nodescraper/plugins/inband/process/process_plugin.py +46 -0
  164. nodescraper/plugins/inband/process/processdata.py +34 -0
  165. nodescraper/plugins/inband/rocm/__init__.py +25 -0
  166. nodescraper/plugins/inband/rocm/analyzer_args.py +66 -0
  167. nodescraper/plugins/inband/rocm/rocm_analyzer.py +100 -0
  168. nodescraper/plugins/inband/rocm/rocm_collector.py +205 -0
  169. nodescraper/plugins/inband/rocm/rocm_plugin.py +43 -0
  170. nodescraper/plugins/inband/rocm/rocmdata.py +62 -0
  171. nodescraper/plugins/inband/storage/__init__.py +25 -0
  172. nodescraper/plugins/inband/storage/analyzer_args.py +38 -0
  173. nodescraper/plugins/inband/storage/collector_args.py +31 -0
  174. nodescraper/plugins/inband/storage/storage_analyzer.py +152 -0
  175. nodescraper/plugins/inband/storage/storage_collector.py +110 -0
  176. nodescraper/plugins/inband/storage/storage_plugin.py +44 -0
  177. nodescraper/plugins/inband/storage/storagedata.py +70 -0
  178. nodescraper/plugins/inband/sysctl/__init__.py +29 -0
  179. nodescraper/plugins/inband/sysctl/analyzer_args.py +67 -0
  180. nodescraper/plugins/inband/sysctl/sysctl_analyzer.py +81 -0
  181. nodescraper/plugins/inband/sysctl/sysctl_collector.py +101 -0
  182. nodescraper/plugins/inband/sysctl/sysctl_plugin.py +43 -0
  183. nodescraper/plugins/inband/sysctl/sysctldata.py +42 -0
  184. nodescraper/plugins/inband/syslog/__init__.py +28 -0
  185. nodescraper/plugins/inband/syslog/syslog_collector.py +121 -0
  186. nodescraper/plugins/inband/syslog/syslog_plugin.py +37 -0
  187. nodescraper/plugins/inband/syslog/syslogdata.py +46 -0
  188. nodescraper/plugins/inband/uptime/__init__.py +25 -0
  189. nodescraper/plugins/inband/uptime/uptime_collector.py +88 -0
  190. nodescraper/plugins/inband/uptime/uptime_plugin.py +37 -0
  191. nodescraper/plugins/inband/uptime/uptimedata.py +31 -0
  192. nodescraper/resultcollators/__init__.py +25 -0
  193. nodescraper/resultcollators/tablesummary.py +159 -0
  194. nodescraper/taskresulthooks/__init__.py +28 -0
  195. nodescraper/taskresulthooks/filesystemloghook.py +88 -0
  196. nodescraper/typeutils.py +171 -0
  197. 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
+ """