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,273 @@
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 re
27
+ from typing import Callable, Optional
28
+
29
+ from pydantic import ValidationError
30
+
31
+ from nodescraper.base import InBandDataCollector
32
+ from nodescraper.connection.inband import CommandArtifact
33
+ from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus, OSFamily
34
+ from nodescraper.models import TaskResult
35
+ from nodescraper.utils import get_exception_details
36
+
37
+ from .analyzer_args import PackageAnalyzerArgs
38
+ from .packagedata import PackageDataModel
39
+
40
+
41
+ class PackageCollector(InBandDataCollector[PackageDataModel, PackageAnalyzerArgs]):
42
+ """Collecting Package information from the system"""
43
+
44
+ DATA_MODEL = PackageDataModel
45
+ CMD_WINDOWS = "wmic product get name,version"
46
+ CMD_RELEASE = "cat /etc/*release"
47
+ CMD_DPKG = "dpkg-query -W"
48
+ CMD_DNF = "dnf list --installed"
49
+ CMD_PACMAN = "pacman -Q"
50
+
51
+ def _detect_package_manager(self) -> Optional[Callable]:
52
+ """Detect the package manager based on the OS release information.
53
+
54
+ Returns:
55
+ Optional[Callable]: A callable function that dumps the packages for the detected package manager,
56
+ or None if the package manager is not supported.
57
+ """
58
+ package_manger_map: dict[str, Callable] = {
59
+ "debian": self._debian_package_dump,
60
+ "redhat": self._dump_fedora_centos_rhel_packages,
61
+ "rhel": self._dump_fedora_centos_rhel_packages,
62
+ "fedora": self._dump_fedora_centos_rhel_packages,
63
+ "centos": self._dump_fedora_centos_rhel_packages,
64
+ "arch": self._dump_arch_packages,
65
+ }
66
+ res = self._run_sut_cmd(self.CMD_RELEASE)
67
+ # search for the package manager key in the release file
68
+ for os, package_manager in package_manger_map.items():
69
+ package_search = re.findall(os, res.stdout, flags=re.IGNORECASE)
70
+ if package_search:
71
+ return package_manager
72
+ return None
73
+
74
+ def _windows_package_dump(self) -> dict[str, str]:
75
+ """Dump installed packages on Windows using wmic
76
+
77
+ Returns:
78
+ dict[str, str]: A dictionary with package names as keys and their versions as values.
79
+ """
80
+ MIN_SPLIT_LENGTH = 2
81
+ res = self._run_sut_cmd(self.CMD_WINDOWS)
82
+ packages = {}
83
+ if res.exit_code != 0:
84
+ self._handle_command_failure(res)
85
+ return {}
86
+ lines = res.stdout.splitlines()
87
+
88
+ for line in lines[1:]:
89
+ columns = line.split()
90
+ if len(columns) <= MIN_SPLIT_LENGTH:
91
+ continue
92
+ # spaces are allowed in names, so we need to join them
93
+ name = (" ").join(columns[:-1])
94
+ version = columns[-1]
95
+ packages[name] = version
96
+
97
+ return packages
98
+
99
+ def _debian_package_dump(self) -> dict[str, str]:
100
+ """Dump installed packages on Debian-based systems using dpkg-query
101
+
102
+ Returns:
103
+ dict[str, str]: A dictionary with package names as keys and their versions as values.
104
+ """
105
+ MIN_SPLIT_LENGTH = 2
106
+ MAX_SPLIT_LENGTH = 3
107
+ res = self._run_sut_cmd(self.CMD_DPKG)
108
+ packages = {}
109
+ if res.exit_code != 0:
110
+ self._handle_command_failure(res)
111
+ return {}
112
+
113
+ lines = res.stdout.splitlines()
114
+ for line in lines:
115
+ columns = line.split()
116
+ if len(columns) < MIN_SPLIT_LENGTH or len(columns) > MAX_SPLIT_LENGTH:
117
+ continue
118
+ if columns[0] == "Installed" or columns[1] == "Packages":
119
+ continue
120
+ packages[columns[0]] = columns[1]
121
+ return packages
122
+
123
+ def _dump_fedora_centos_rhel_packages(self) -> dict[str, str]:
124
+ """Dump installed packages on Fedora, CentOS, or RHEL using dnf
125
+
126
+ Returns:
127
+ dict[str, str]: A dictionary with package names as keys and their versions as values.
128
+ """
129
+ MIN_SPLIT_LENGTH = 2
130
+ MAX_SPLIT_LENGTH = 3
131
+ res = self._run_sut_cmd(self.CMD_DNF)
132
+ packages = {}
133
+ if res.exit_code != 0:
134
+ self._handle_command_failure(res)
135
+ return {}
136
+ lines = res.stdout.splitlines()
137
+ for line in lines:
138
+ columns = line.split()
139
+ if len(columns) < MIN_SPLIT_LENGTH or len(columns) > MAX_SPLIT_LENGTH:
140
+ continue
141
+ if "Installed" in columns[0] or "Packages" in columns[1]:
142
+ continue
143
+ packages[columns[0]] = columns[1]
144
+ return packages
145
+
146
+ def _dump_arch_packages(self) -> dict[str, str]:
147
+ """Dump installed packages on Arch Linux using pacman
148
+
149
+ Returns:
150
+ dict[str, str]: A dictionary with package names as keys and their versions as values.
151
+ """
152
+ EXPECTED_SPLIT_LENGTH = 2
153
+ res: CommandArtifact = self._run_sut_cmd(self.CMD_PACMAN)
154
+ packages = {}
155
+ if res.exit_code != 0:
156
+ self._handle_command_failure(res)
157
+ return {}
158
+ lines = res.stdout.splitlines()
159
+ for line in lines:
160
+ columns = line.split()
161
+ if len(columns) != EXPECTED_SPLIT_LENGTH:
162
+ continue
163
+ packages[columns[0]] = columns[1]
164
+ return packages
165
+
166
+ def _handle_command_failure(self, command_artifact: CommandArtifact):
167
+ """Handle command failure by logging the error and updating the result.
168
+ This method logs the error details and updates the result with a failure status.
169
+
170
+ Args:
171
+ command_artifact (CommandArtifact): The command artifact containing the command output and error details.
172
+ """
173
+ self._log_event(
174
+ category=EventCategory.OS,
175
+ description=f"Error running command: {command_artifact.command}",
176
+ priority=EventPriority.WARNING,
177
+ data={
178
+ "stderr": command_artifact.stderr,
179
+ "exit_code": command_artifact.exit_code,
180
+ },
181
+ )
182
+ self.result.message = "Failed to run Package Manager command"
183
+ self.result.status = ExecutionStatus.EXECUTION_FAILURE
184
+
185
+ def _filter_rocm_packages(self, packages: dict[str, str], rocm_pattern: str) -> dict[str, str]:
186
+ """Filter ROCm-related packages from a package dictionary.
187
+
188
+ This method searches package names for ROCm-related patterns and returns
189
+ only the matching packages.
190
+
191
+ Args:
192
+ packages (dict[str, str]): Dictionary with package names as keys and versions as values.
193
+ rocm_pattern (str): Regex pattern to match ROCm-related package names.
194
+
195
+ Returns:
196
+ dict[str, str]: Filtered dictionary containing only ROCm-related packages.
197
+ """
198
+ rocm_packages = {}
199
+ pattern = re.compile(rocm_pattern, re.IGNORECASE)
200
+ for package_name, version in packages.items():
201
+ if pattern.search(package_name):
202
+ rocm_packages[package_name] = version
203
+ return rocm_packages
204
+
205
+ def collect_data(
206
+ self, args: Optional[PackageAnalyzerArgs] = None
207
+ ) -> tuple[TaskResult, Optional[PackageDataModel]]:
208
+ """Collect package information from the system.
209
+
210
+ Args:
211
+ args (Optional[PackageAnalyzerArgs]): Optional arguments containing ROCm regex pattern.
212
+
213
+ Returns:
214
+ tuple[TaskResult, Optional[PackageDataModel]]: tuple containing the task result and a PackageDataModel instance
215
+ with the collected package information, or None if there was an error.
216
+ """
217
+ packages = {}
218
+ # Windows
219
+ if self.system_info.os_family == OSFamily.WINDOWS:
220
+ packages = self._windows_package_dump()
221
+ # Linux
222
+ elif self.system_info.os_family == OSFamily.LINUX:
223
+ package_manager = self._detect_package_manager()
224
+ if package_manager:
225
+ packages = package_manager()
226
+ else:
227
+ self.result.message = "Unsupported package manager"
228
+ self.result.status = ExecutionStatus.NOT_RAN
229
+ return self.result, None
230
+ else:
231
+ self.result.message = "Unsupported OS"
232
+ self.result.status = ExecutionStatus.NOT_RAN
233
+ return self.result, None
234
+
235
+ # Filter and log ROCm packages if on Linux and rocm_regex is provided
236
+ if self.system_info.os_family == OSFamily.LINUX and packages:
237
+ # Get ROCm pattern from args if provided
238
+ rocm_pattern = args.rocm_regex if args else None
239
+ if rocm_pattern:
240
+ self.logger.info("Using rocm_pattern: %s", rocm_pattern)
241
+ rocm_packages = self._filter_rocm_packages(packages, rocm_pattern)
242
+ if rocm_packages:
243
+ self.result.message = (
244
+ f"Found {len(rocm_packages)} ROCm-related packages installed"
245
+ )
246
+ self.result.status = ExecutionStatus.OK
247
+ self._log_event(
248
+ category=EventCategory.OS,
249
+ description=f"Found {len(rocm_packages)} ROCm-related packages installed",
250
+ priority=EventPriority.INFO,
251
+ data={"rocm_packages": sorted(rocm_packages.keys())},
252
+ )
253
+ else:
254
+ self.logger.info("No rocm_regex provided, skipping ROCm package filtering")
255
+
256
+ # Extract rocm_regex and enable_rocm_regex from args if provided
257
+ rocm_regex = args.rocm_regex if (args and args.rocm_regex) else ""
258
+ enable_rocm_regex = getattr(args, "enable_rocm_regex", False) if args else False
259
+
260
+ try:
261
+ package_model = PackageDataModel(
262
+ version_info=packages, rocm_regex=rocm_regex, enable_rocm_regex=enable_rocm_regex
263
+ )
264
+ except ValidationError as val_err:
265
+ self._log_event(
266
+ category=EventCategory.RUNTIME,
267
+ description="Error validating package data",
268
+ priority=EventPriority.WARNING,
269
+ data=get_exception_details(val_err),
270
+ )
271
+ package_model = None
272
+
273
+ return self.result, package_model
@@ -0,0 +1,43 @@
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 nodescraper.base import InBandDataPlugin
27
+
28
+ from .analyzer_args import PackageAnalyzerArgs
29
+ from .package_analyzer import PackageAnalyzer
30
+ from .package_collector import PackageCollector
31
+ from .packagedata import PackageDataModel
32
+
33
+
34
+ class PackagePlugin(InBandDataPlugin[PackageDataModel, None, PackageAnalyzerArgs]):
35
+ """Plugin for collection and analysis of package version data"""
36
+
37
+ DATA_MODEL = PackageDataModel
38
+
39
+ COLLECTOR = PackageCollector
40
+
41
+ ANALYZER = PackageAnalyzer
42
+
43
+ ANALYZER_ARGS = PackageAnalyzerArgs
@@ -0,0 +1,41 @@
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 nodescraper.models import DataModel
27
+
28
+
29
+ class PackageDataModel(DataModel):
30
+ """Pacakge data contains the package data for the system
31
+
32
+ Attributes:
33
+ version_info (dict[str, str]): The version information for the package
34
+ Key is the package name and value is the version of the package
35
+ rocm_regex (str): Regular expression pattern for ROCm package filtering
36
+ enable_rocm_regex (bool): Whether to use custom ROCm regex from collection_args
37
+ """
38
+
39
+ version_info: dict[str, str]
40
+ rocm_regex: str = ""
41
+ enable_rocm_regex: bool = False
@@ -0,0 +1,29 @@
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 .analyzer_args import PcieAnalyzerArgs
27
+ from .pcie_plugin import PciePlugin
28
+
29
+ __all__ = ["PciePlugin", "PcieAnalyzerArgs"]
@@ -0,0 +1,63 @@
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 Dict, Optional, Union
27
+
28
+ from nodescraper.models import AnalyzerArgs
29
+
30
+
31
+ class PcieAnalyzerArgs(AnalyzerArgs):
32
+ """Arguments for PCIe analyzer
33
+
34
+ Attributes:
35
+ exp_speed: Expected PCIe speed (generation 1-5)
36
+ exp_width: Expected PCIe width (1-16 lanes)
37
+ exp_sriov_count: Expected SR-IOV VF count
38
+ exp_gpu_count_override: Override expected GPU count
39
+ exp_max_payload_size: Expected max payload size (int for all devices, dict for specific device IDs)
40
+ exp_max_rd_req_size: Expected max read request size (int for all devices, dict for specific device IDs)
41
+ exp_ten_bit_tag_req_en: Expected 10-bit tag request enable (int for all devices, dict for specific device IDs)
42
+ """
43
+
44
+ exp_speed: int = 5
45
+ exp_width: int = 16
46
+ exp_sriov_count: int = 0
47
+ exp_gpu_count_override: Optional[int] = None
48
+ exp_max_payload_size: Optional[Union[Dict[int, int], int]] = None
49
+ exp_max_rd_req_size: Optional[Union[Dict[int, int], int]] = None
50
+ exp_ten_bit_tag_req_en: Optional[Union[Dict[int, int], int]] = None
51
+
52
+
53
+ def normalize_to_dict(
54
+ value: Optional[Union[Dict[int, int], int]], vendorid_ep: int
55
+ ) -> Dict[int, int]:
56
+ """Normalize int or dict values to dict format using vendorid_ep as key for int values"""
57
+ if value is None:
58
+ return {}
59
+ if isinstance(value, int):
60
+ return {vendorid_ep: value}
61
+ if isinstance(value, dict):
62
+ return value
63
+ return {}