amd-node-scraper 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- amd_node_scraper-0.0.1.dist-info/LICENSE +21 -0
- amd_node_scraper-0.0.1.dist-info/METADATA +424 -0
- amd_node_scraper-0.0.1.dist-info/RECORD +197 -0
- amd_node_scraper-0.0.1.dist-info/WHEEL +5 -0
- amd_node_scraper-0.0.1.dist-info/entry_points.txt +2 -0
- amd_node_scraper-0.0.1.dist-info/top_level.txt +1 -0
- nodescraper/__init__.py +32 -0
- nodescraper/base/__init__.py +34 -0
- nodescraper/base/inbandcollectortask.py +118 -0
- nodescraper/base/inbanddataplugin.py +39 -0
- nodescraper/base/regexanalyzer.py +120 -0
- nodescraper/cli/__init__.py +29 -0
- nodescraper/cli/cli.py +511 -0
- nodescraper/cli/constants.py +27 -0
- nodescraper/cli/dynamicparserbuilder.py +171 -0
- nodescraper/cli/helper.py +517 -0
- nodescraper/cli/inputargtypes.py +129 -0
- nodescraper/configbuilder.py +123 -0
- nodescraper/configregistry.py +66 -0
- nodescraper/configs/node_status.json +19 -0
- nodescraper/connection/__init__.py +25 -0
- nodescraper/connection/inband/__init__.py +46 -0
- nodescraper/connection/inband/inband.py +171 -0
- nodescraper/connection/inband/inbandlocal.py +93 -0
- nodescraper/connection/inband/inbandmanager.py +151 -0
- nodescraper/connection/inband/inbandremote.py +173 -0
- nodescraper/connection/inband/sshparams.py +43 -0
- nodescraper/constants.py +26 -0
- nodescraper/enums/__init__.py +40 -0
- nodescraper/enums/eventcategory.py +89 -0
- nodescraper/enums/eventpriority.py +42 -0
- nodescraper/enums/executionstatus.py +44 -0
- nodescraper/enums/osfamily.py +34 -0
- nodescraper/enums/systeminteraction.py +41 -0
- nodescraper/enums/systemlocation.py +33 -0
- nodescraper/generictypes.py +36 -0
- nodescraper/interfaces/__init__.py +44 -0
- nodescraper/interfaces/connectionmanager.py +143 -0
- nodescraper/interfaces/dataanalyzertask.py +138 -0
- nodescraper/interfaces/datacollectortask.py +185 -0
- nodescraper/interfaces/dataplugin.py +356 -0
- nodescraper/interfaces/plugin.py +127 -0
- nodescraper/interfaces/resultcollator.py +56 -0
- nodescraper/interfaces/task.py +164 -0
- nodescraper/interfaces/taskresulthook.py +39 -0
- nodescraper/models/__init__.py +48 -0
- nodescraper/models/analyzerargs.py +93 -0
- nodescraper/models/collectorargs.py +30 -0
- nodescraper/models/connectionconfig.py +34 -0
- nodescraper/models/datamodel.py +171 -0
- nodescraper/models/datapluginresult.py +39 -0
- nodescraper/models/event.py +158 -0
- nodescraper/models/pluginconfig.py +38 -0
- nodescraper/models/pluginresult.py +39 -0
- nodescraper/models/systeminfo.py +44 -0
- nodescraper/models/taskresult.py +185 -0
- nodescraper/models/timerangeargs.py +38 -0
- nodescraper/pluginexecutor.py +274 -0
- nodescraper/pluginregistry.py +152 -0
- nodescraper/plugins/__init__.py +25 -0
- nodescraper/plugins/inband/__init__.py +25 -0
- nodescraper/plugins/inband/amdsmi/__init__.py +28 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_analyzer.py +821 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_collector.py +1313 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_plugin.py +43 -0
- nodescraper/plugins/inband/amdsmi/amdsmidata.py +1002 -0
- nodescraper/plugins/inband/amdsmi/analyzer_args.py +50 -0
- nodescraper/plugins/inband/amdsmi/cper.py +65 -0
- nodescraper/plugins/inband/bios/__init__.py +29 -0
- nodescraper/plugins/inband/bios/analyzer_args.py +64 -0
- nodescraper/plugins/inband/bios/bios_analyzer.py +93 -0
- nodescraper/plugins/inband/bios/bios_collector.py +93 -0
- nodescraper/plugins/inband/bios/bios_plugin.py +43 -0
- nodescraper/plugins/inband/bios/biosdata.py +30 -0
- nodescraper/plugins/inband/cmdline/__init__.py +25 -0
- nodescraper/plugins/inband/cmdline/analyzer_args.py +80 -0
- nodescraper/plugins/inband/cmdline/cmdline_analyzer.py +113 -0
- nodescraper/plugins/inband/cmdline/cmdline_collector.py +77 -0
- nodescraper/plugins/inband/cmdline/cmdline_plugin.py +43 -0
- nodescraper/plugins/inband/cmdline/cmdlinedata.py +30 -0
- nodescraper/plugins/inband/device_enumeration/__init__.py +29 -0
- nodescraper/plugins/inband/device_enumeration/analyzer_args.py +73 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_analyzer.py +81 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_collector.py +176 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_plugin.py +45 -0
- nodescraper/plugins/inband/device_enumeration/deviceenumdata.py +36 -0
- nodescraper/plugins/inband/dimm/__init__.py +25 -0
- nodescraper/plugins/inband/dimm/collector_args.py +31 -0
- nodescraper/plugins/inband/dimm/dimm_collector.py +151 -0
- nodescraper/plugins/inband/dimm/dimm_plugin.py +40 -0
- nodescraper/plugins/inband/dimm/dimmdata.py +30 -0
- nodescraper/plugins/inband/dkms/__init__.py +25 -0
- nodescraper/plugins/inband/dkms/analyzer_args.py +85 -0
- nodescraper/plugins/inband/dkms/dkms_analyzer.py +106 -0
- nodescraper/plugins/inband/dkms/dkms_collector.py +76 -0
- nodescraper/plugins/inband/dkms/dkms_plugin.py +43 -0
- nodescraper/plugins/inband/dkms/dkmsdata.py +33 -0
- nodescraper/plugins/inband/dmesg/__init__.py +28 -0
- nodescraper/plugins/inband/dmesg/analyzer_args.py +33 -0
- nodescraper/plugins/inband/dmesg/collector_args.py +39 -0
- nodescraper/plugins/inband/dmesg/dmesg_analyzer.py +503 -0
- nodescraper/plugins/inband/dmesg/dmesg_collector.py +164 -0
- nodescraper/plugins/inband/dmesg/dmesg_plugin.py +44 -0
- nodescraper/plugins/inband/dmesg/dmesgdata.py +116 -0
- nodescraper/plugins/inband/fabrics/__init__.py +28 -0
- nodescraper/plugins/inband/fabrics/fabrics_collector.py +726 -0
- nodescraper/plugins/inband/fabrics/fabrics_plugin.py +37 -0
- nodescraper/plugins/inband/fabrics/fabricsdata.py +140 -0
- nodescraper/plugins/inband/journal/__init__.py +28 -0
- nodescraper/plugins/inband/journal/collector_args.py +33 -0
- nodescraper/plugins/inband/journal/journal_collector.py +107 -0
- nodescraper/plugins/inband/journal/journal_plugin.py +40 -0
- nodescraper/plugins/inband/journal/journaldata.py +44 -0
- nodescraper/plugins/inband/kernel/__init__.py +25 -0
- nodescraper/plugins/inband/kernel/analyzer_args.py +64 -0
- nodescraper/plugins/inband/kernel/kernel_analyzer.py +91 -0
- nodescraper/plugins/inband/kernel/kernel_collector.py +129 -0
- nodescraper/plugins/inband/kernel/kernel_plugin.py +43 -0
- nodescraper/plugins/inband/kernel/kerneldata.py +32 -0
- nodescraper/plugins/inband/kernel_module/__init__.py +25 -0
- nodescraper/plugins/inband/kernel_module/analyzer_args.py +59 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_analyzer.py +211 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_collector.py +264 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_data.py +60 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_plugin.py +43 -0
- nodescraper/plugins/inband/memory/__init__.py +25 -0
- nodescraper/plugins/inband/memory/analyzer_args.py +45 -0
- nodescraper/plugins/inband/memory/memory_analyzer.py +98 -0
- nodescraper/plugins/inband/memory/memory_collector.py +330 -0
- nodescraper/plugins/inband/memory/memory_plugin.py +43 -0
- nodescraper/plugins/inband/memory/memorydata.py +90 -0
- nodescraper/plugins/inband/network/__init__.py +28 -0
- nodescraper/plugins/inband/network/network_collector.py +1828 -0
- nodescraper/plugins/inband/network/network_plugin.py +37 -0
- nodescraper/plugins/inband/network/networkdata.py +319 -0
- nodescraper/plugins/inband/nvme/__init__.py +28 -0
- nodescraper/plugins/inband/nvme/nvme_collector.py +167 -0
- nodescraper/plugins/inband/nvme/nvme_plugin.py +37 -0
- nodescraper/plugins/inband/nvme/nvmedata.py +45 -0
- nodescraper/plugins/inband/os/__init__.py +25 -0
- nodescraper/plugins/inband/os/analyzer_args.py +64 -0
- nodescraper/plugins/inband/os/os_analyzer.py +73 -0
- nodescraper/plugins/inband/os/os_collector.py +131 -0
- nodescraper/plugins/inband/os/os_plugin.py +43 -0
- nodescraper/plugins/inband/os/osdata.py +31 -0
- nodescraper/plugins/inband/package/__init__.py +25 -0
- nodescraper/plugins/inband/package/analyzer_args.py +48 -0
- nodescraper/plugins/inband/package/package_analyzer.py +253 -0
- nodescraper/plugins/inband/package/package_collector.py +273 -0
- nodescraper/plugins/inband/package/package_plugin.py +43 -0
- nodescraper/plugins/inband/package/packagedata.py +41 -0
- nodescraper/plugins/inband/pcie/__init__.py +29 -0
- nodescraper/plugins/inband/pcie/analyzer_args.py +63 -0
- nodescraper/plugins/inband/pcie/pcie_analyzer.py +1081 -0
- nodescraper/plugins/inband/pcie/pcie_collector.py +690 -0
- nodescraper/plugins/inband/pcie/pcie_data.py +2017 -0
- nodescraper/plugins/inband/pcie/pcie_plugin.py +43 -0
- nodescraper/plugins/inband/process/__init__.py +25 -0
- nodescraper/plugins/inband/process/analyzer_args.py +45 -0
- nodescraper/plugins/inband/process/collector_args.py +31 -0
- nodescraper/plugins/inband/process/process_analyzer.py +91 -0
- nodescraper/plugins/inband/process/process_collector.py +115 -0
- nodescraper/plugins/inband/process/process_plugin.py +46 -0
- nodescraper/plugins/inband/process/processdata.py +34 -0
- nodescraper/plugins/inband/rocm/__init__.py +25 -0
- nodescraper/plugins/inband/rocm/analyzer_args.py +66 -0
- nodescraper/plugins/inband/rocm/rocm_analyzer.py +100 -0
- nodescraper/plugins/inband/rocm/rocm_collector.py +205 -0
- nodescraper/plugins/inband/rocm/rocm_plugin.py +43 -0
- nodescraper/plugins/inband/rocm/rocmdata.py +62 -0
- nodescraper/plugins/inband/storage/__init__.py +25 -0
- nodescraper/plugins/inband/storage/analyzer_args.py +38 -0
- nodescraper/plugins/inband/storage/collector_args.py +31 -0
- nodescraper/plugins/inband/storage/storage_analyzer.py +152 -0
- nodescraper/plugins/inband/storage/storage_collector.py +110 -0
- nodescraper/plugins/inband/storage/storage_plugin.py +44 -0
- nodescraper/plugins/inband/storage/storagedata.py +70 -0
- nodescraper/plugins/inband/sysctl/__init__.py +29 -0
- nodescraper/plugins/inband/sysctl/analyzer_args.py +67 -0
- nodescraper/plugins/inband/sysctl/sysctl_analyzer.py +81 -0
- nodescraper/plugins/inband/sysctl/sysctl_collector.py +101 -0
- nodescraper/plugins/inband/sysctl/sysctl_plugin.py +43 -0
- nodescraper/plugins/inband/sysctl/sysctldata.py +42 -0
- nodescraper/plugins/inband/syslog/__init__.py +28 -0
- nodescraper/plugins/inband/syslog/syslog_collector.py +121 -0
- nodescraper/plugins/inband/syslog/syslog_plugin.py +37 -0
- nodescraper/plugins/inband/syslog/syslogdata.py +46 -0
- nodescraper/plugins/inband/uptime/__init__.py +25 -0
- nodescraper/plugins/inband/uptime/uptime_collector.py +88 -0
- nodescraper/plugins/inband/uptime/uptime_plugin.py +37 -0
- nodescraper/plugins/inband/uptime/uptimedata.py +31 -0
- nodescraper/resultcollators/__init__.py +25 -0
- nodescraper/resultcollators/tablesummary.py +159 -0
- nodescraper/taskresulthooks/__init__.py +28 -0
- nodescraper/taskresulthooks/filesystemloghook.py +88 -0
- nodescraper/typeutils.py +171 -0
- nodescraper/utils.py +412 -0
|
@@ -0,0 +1,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 {}
|