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,164 @@
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 copy
28
+ import datetime
29
+ import logging
30
+ from typing import Any, Optional, Union
31
+
32
+ from nodescraper.constants import DEFAULT_LOGGER
33
+ from nodescraper.enums import EventCategory, EventPriority
34
+ from nodescraper.models import Event, SystemInfo, TaskResult
35
+
36
+ from .taskresulthook import TaskResultHook
37
+
38
+
39
+ class SystemCompatibilityError(Exception):
40
+ """Exception raised when system has invalid attributes for the task"""
41
+
42
+ pass
43
+
44
+
45
+ class Task(abc.ABC):
46
+ """Parent class for all tasks"""
47
+
48
+ TASK_TYPE: str
49
+
50
+ def __init__(
51
+ self,
52
+ system_info: SystemInfo,
53
+ logger: Optional[logging.Logger] = None,
54
+ max_event_priority_level: Union[EventPriority, str] = EventPriority.CRITICAL,
55
+ parent: Optional[str] = None,
56
+ task_result_hooks: Optional[list[TaskResultHook]] = None,
57
+ **kwargs: dict[str, Any],
58
+ ):
59
+ if logger is None:
60
+ logger = logging.getLogger(DEFAULT_LOGGER)
61
+ self.system_info = system_info
62
+ self.logger = logger
63
+ self.max_event_priority_level = max_event_priority_level
64
+ self.parent = parent
65
+ if not task_result_hooks:
66
+ task_result_hooks = []
67
+ self.task_result_hooks = task_result_hooks
68
+ self.result: TaskResult = self._init_result()
69
+
70
+ @property
71
+ def max_event_priority_level(self) -> EventPriority:
72
+ """maximum priority level for events generated by task
73
+
74
+ Returns:
75
+ EventPriority: max priority level enum
76
+ """
77
+ return self._max_event_priority_level
78
+
79
+ @max_event_priority_level.setter
80
+ def max_event_priority_level(self, input_value: Union[str, EventPriority]):
81
+ if isinstance(input_value, str):
82
+ value: EventPriority = getattr(EventPriority, input_value)
83
+ elif isinstance(input_value, int):
84
+ value = EventPriority(input_value)
85
+ elif isinstance(input_value, EventPriority):
86
+ value: EventPriority = input_value # type:ignore
87
+ else:
88
+ raise ValueError(f"Invalid type for max_event_priority_level: {type(input_value)}")
89
+
90
+ self._max_event_priority_level = value
91
+
92
+ def __init_subclass__(cls, **kwargs) -> None:
93
+ super().__init_subclass__(**kwargs)
94
+ if cls.TASK_TYPE is None:
95
+ raise TypeError(f"No value provided for TASK_TYPE in task class {cls.__name__}")
96
+
97
+ def _build_event(
98
+ self,
99
+ category: Union[EventCategory, str],
100
+ description: str,
101
+ priority: EventPriority,
102
+ data: Optional[dict] = None,
103
+ timestamp: Optional[datetime.datetime] = None,
104
+ ) -> Event:
105
+
106
+ if data is None:
107
+ data = {"task_name": self.__class__.__name__, "task_type": self.TASK_TYPE}
108
+
109
+ else:
110
+ # Copy to avoid mutating the caller's dict
111
+ data = copy.copy(data)
112
+ data["task_name"] = self.__class__.__name__
113
+ data["task_type"] = self.TASK_TYPE
114
+
115
+ if self.parent:
116
+ data["parent"] = self.parent
117
+
118
+ if self.system_info.metadata:
119
+ data["system_metadata"] = copy.copy(self.system_info.metadata)
120
+
121
+ if priority > self.max_event_priority_level:
122
+ priority = self.max_event_priority_level
123
+
124
+ event = Event(
125
+ category=category,
126
+ description=description,
127
+ priority=priority,
128
+ data=data,
129
+ )
130
+
131
+ if timestamp:
132
+ event.timestamp = timestamp
133
+
134
+ return event
135
+
136
+ def _log_event(
137
+ self,
138
+ category: Union[EventCategory, str],
139
+ description: str,
140
+ priority: EventPriority,
141
+ data: Optional[dict] = None,
142
+ timestamp: Optional[datetime.datetime] = None,
143
+ console_log: bool = False,
144
+ ):
145
+ event = self._build_event(
146
+ category=category,
147
+ description=description,
148
+ priority=priority,
149
+ data=data,
150
+ timestamp=timestamp,
151
+ )
152
+
153
+ if console_log:
154
+ self.logger.log(getattr(logging, priority.name, logging.INFO), description)
155
+
156
+ self.result.events.append(event)
157
+
158
+ def _init_result(self):
159
+ result = TaskResult(task=self.__class__.__name__, parent=self.parent)
160
+ return result
161
+
162
+ def _run_hooks(self, result, **kwargs):
163
+ for hook in self.task_result_hooks:
164
+ hook.process_result(result, **kwargs)
@@ -0,0 +1,39 @@
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
+
28
+ from nodescraper.models import TaskResult
29
+
30
+
31
+ class TaskResultHook(abc.ABC):
32
+
33
+ @abc.abstractmethod
34
+ def process_result(self, task_result: TaskResult, **kwargs):
35
+ """post process task result
36
+
37
+ Args:
38
+ task_result (TaskResult): input task result
39
+ """
@@ -0,0 +1,48 @@
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
+ from .analyzerargs import AnalyzerArgs
27
+ from .collectorargs import CollectorArgs
28
+ from .datamodel import DataModel
29
+ from .datapluginresult import DataPluginResult
30
+ from .event import Event
31
+ from .pluginconfig import PluginConfig
32
+ from .pluginresult import PluginResult
33
+ from .systeminfo import SystemInfo
34
+ from .taskresult import TaskResult
35
+ from .timerangeargs import TimeRangeAnalysisArgs
36
+
37
+ __all__ = [
38
+ "AnalyzerArgs",
39
+ "CollectorArgs",
40
+ "DataModel",
41
+ "TaskResult",
42
+ "Event",
43
+ "SystemInfo",
44
+ "PluginResult",
45
+ "DataPluginResult",
46
+ "PluginConfig",
47
+ "TimeRangeAnalysisArgs",
48
+ ]
@@ -0,0 +1,93 @@
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
+ from typing import Any
27
+
28
+ from pydantic import BaseModel, model_validator
29
+
30
+
31
+ class AnalyzerArgs(BaseModel):
32
+ """Base class for all analyzer arguments.
33
+
34
+ This class provides automatic string stripping for all string values
35
+ in analyzer args. All analyzer args classes should inherit from this
36
+ directly.
37
+
38
+ """
39
+
40
+ model_config = {"extra": "forbid", "exclude_none": True}
41
+
42
+ @model_validator(mode="before")
43
+ @classmethod
44
+ def strip_string_values(cls, data: Any) -> Any:
45
+ """Strip whitespace from all string values in analyzer args.
46
+
47
+ This validator recursively processes:
48
+ - String values: strips whitespace
49
+ - Lists: strips strings in lists
50
+ - Dicts: strips string values in dicts
51
+ - Other types: left unchanged
52
+
53
+ Args:
54
+ data: The input data to validate
55
+
56
+ Returns:
57
+ The data with all string values stripped
58
+ """
59
+ if isinstance(data, dict):
60
+ return {k: cls._strip_value(v) for k, v in data.items()}
61
+ return data
62
+
63
+ @classmethod
64
+ def _strip_value(cls, value: Any) -> Any:
65
+ """Recursively strip string values.
66
+
67
+ Args:
68
+ value: The value to process
69
+
70
+ Returns:
71
+ The processed value
72
+ """
73
+ if isinstance(value, str):
74
+ return value.strip()
75
+ elif isinstance(value, list):
76
+ return [cls._strip_value(item) for item in value]
77
+ elif isinstance(value, dict):
78
+ return {k: cls._strip_value(v) for k, v in value.items()}
79
+ return value
80
+
81
+ @classmethod
82
+ def build_from_model(cls, datamodel):
83
+ """Build analyzer args instance from data model object
84
+
85
+ Args:
86
+ datamodel (TDataModel): data model to use for creating analyzer args
87
+
88
+ Raises:
89
+ NotImplementedError: Not implemented error
90
+ """
91
+ raise NotImplementedError(
92
+ "Setting analyzer args from datamodel is not implemented for class: %s", cls.__name__
93
+ )
@@ -0,0 +1,30 @@
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
+ from pydantic import BaseModel
27
+
28
+
29
+ class CollectorArgs(BaseModel):
30
+ model_config = {"extra": "forbid", "exclude_none": True}
@@ -0,0 +1,34 @@
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
+ from pydantic import BaseModel, Field
27
+
28
+
29
+ class ConnectionConfig(BaseModel):
30
+ """
31
+ Model that defines the structure for connection configurations.
32
+ """
33
+
34
+ connection: dict[str, dict] = Field(default_factory=dict)
@@ -0,0 +1,171 @@
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 io
27
+ import json
28
+ import os
29
+ import tarfile
30
+ from typing import TypeVar, Union
31
+
32
+ from pydantic import BaseModel, Field, field_validator
33
+
34
+ from nodescraper.utils import get_unique_filename
35
+
36
+ TDataModel = TypeVar("TDataModel", bound="DataModel")
37
+
38
+
39
+ class FileModel(BaseModel):
40
+ file_contents: bytes = Field(exclude=True)
41
+ file_name: str
42
+
43
+ @field_validator("file_contents", mode="before")
44
+ @classmethod
45
+ def file_contents_conformer(cls, value: Union[io.BytesIO, str, bytes]) -> bytes:
46
+ if isinstance(value, io.BytesIO):
47
+ return value.getvalue()
48
+ if isinstance(value, str):
49
+ return value.encode("utf-8")
50
+ return value
51
+
52
+ def log_model(self, log_path: str) -> None:
53
+ """Log data model to a file
54
+
55
+ Args:
56
+ log_path (str): log path
57
+ """
58
+ log_name = os.path.join(log_path, self.file_name)
59
+ with open(log_name, "wb") as log_file:
60
+ log_file.write(self.file_contents)
61
+
62
+ def file_contents_str(self) -> None:
63
+ return self.file_contents.decode("utf-8")
64
+
65
+
66
+ class DataModel(BaseModel):
67
+ """Base class for data model, used to define structure of data collected from the system"""
68
+
69
+ def log_model(self, log_path: str):
70
+ """Log data model to a file
71
+
72
+ Args:
73
+ log_path (str): log path
74
+ """
75
+ log_name = os.path.join(
76
+ log_path,
77
+ get_unique_filename(log_path, f"{self.__class__.__name__.lower()}.json"),
78
+ )
79
+
80
+ exlude_fields = set()
81
+ for key in self.model_fields:
82
+ data = getattr(self, key)
83
+ if isinstance(data, FileModel):
84
+ data.log_model(log_path)
85
+ exlude_fields.add(key)
86
+
87
+ with open(log_name, "w", encoding="utf-8") as log_file:
88
+ log_file.write(self.model_dump_json(indent=2, exclude=exlude_fields))
89
+
90
+ def merge_data(self, input_data: "DataModel") -> None:
91
+ """Merge data into current data"""
92
+ pass
93
+
94
+ @classmethod
95
+ def import_model(cls: type[TDataModel], model_input: Union[dict, str]) -> TDataModel:
96
+ """import a data model
97
+ if the input is a string attempt to read data from file using the string as a file name
98
+ if input is a dict, pass key value pairs directly to init function
99
+
100
+
101
+ Args:
102
+ cls (type[DataModel]): Data model class
103
+ model_input (Union[dict, str]): model data input
104
+
105
+ Raises:
106
+ ValueError: if model_input has an invalid type
107
+
108
+ Returns:
109
+ DataModel: instance of the data model
110
+ """
111
+
112
+ if isinstance(model_input, dict):
113
+ return cls(**model_input)
114
+
115
+ if isinstance(model_input, str):
116
+ # Build from tarfile if supported
117
+ if tarfile.is_tarfile(model_input):
118
+ return cls.build_from_tar(model_input)
119
+ # Build from folder if supported
120
+ if os.path.isdir(model_input):
121
+ return cls.build_from_folder(model_input)
122
+
123
+ # Build from json file
124
+ with open(model_input, "r", encoding="utf-8") as input_file:
125
+ data = json.load(input_file)
126
+
127
+ return cls(**data)
128
+
129
+ raise ValueError("Invalid input for model data")
130
+
131
+ @classmethod
132
+ def build_from_tar(cls: type[TDataModel], tar_path: str) -> TDataModel:
133
+ """Placeholder for building data model from tarfile.
134
+
135
+ Intended for use with models that contains multiple FileModel attributes, and when collected they
136
+ are in a tarfile. This is left blank if the model requires this then this function should be implemented.
137
+
138
+ Parameters
139
+ ----------
140
+ cls : type[DataModelGeneric@build_from_tar]
141
+ A DataModel class
142
+ tar_path : str
143
+ A path to a folder containing the data in format .tar.xz
144
+
145
+ Returns
146
+ -------
147
+ DataModelGeneric@build_from_tar
148
+ A datamodel object of type cls
149
+ """
150
+ raise NotImplementedError("Model does not support construction from tar")
151
+
152
+ @classmethod
153
+ def build_from_folder(cls: type[TDataModel], folder_path: str) -> TDataModel:
154
+ """Placeholder for building data model from folder.
155
+
156
+ Intended for use with models that contains multiple FileModel attributes. This is left blank
157
+ if that model requires this then this function should be implemented.
158
+
159
+ Parameters
160
+ ----------
161
+ cls : type[DataModelGeneric@build_from_folder]
162
+ A DataModel class
163
+ folder_path : str
164
+ A path to a folder containing the data in format .tar.xz
165
+
166
+ Returns
167
+ -------
168
+ DataModelGeneric@build_from_folder
169
+ A datamodel object of type cls
170
+ """
171
+ raise NotImplementedError("Model does not support construction from folder")
@@ -0,0 +1,39 @@
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
+ from typing import Optional
27
+
28
+ from pydantic import BaseModel
29
+
30
+ from .datamodel import DataModel
31
+ from .taskresult import TaskResult
32
+
33
+
34
+ class DataPluginResult(BaseModel):
35
+ """Object for result of data plugin"""
36
+
37
+ system_data: Optional[DataModel] = None
38
+ collection_result: Optional[TaskResult] = None
39
+ analysis_result: Optional[TaskResult] = None