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,264 @@
|
|
|
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 nodescraper.base import InBandDataCollector
|
|
29
|
+
from nodescraper.connection.inband import TextFileArtifact
|
|
30
|
+
from nodescraper.connection.inband.inband import CommandArtifact
|
|
31
|
+
from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus, OSFamily
|
|
32
|
+
from nodescraper.models import TaskResult
|
|
33
|
+
|
|
34
|
+
from .kernel_module_data import KernelModuleDataModel, ModuleInfo, ModuleParameter
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class KernelModuleCollector(InBandDataCollector[KernelModuleDataModel, None]):
|
|
38
|
+
"""Read kernel modules and associated parameters"""
|
|
39
|
+
|
|
40
|
+
DATA_MODEL = KernelModuleDataModel
|
|
41
|
+
CMD_WINDOWS = "wmic os get Version /Value"
|
|
42
|
+
CMD = "cat /proc/modules"
|
|
43
|
+
CMD_MODINFO_AMDGPU = "modinfo amdgpu"
|
|
44
|
+
|
|
45
|
+
def parse_proc_modules(self, output: dict) -> dict:
|
|
46
|
+
"""Parse command output and return dict of modules
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
output (dict): sut cmd output
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
dict: parsed modules
|
|
53
|
+
"""
|
|
54
|
+
modules = {}
|
|
55
|
+
for line in output.strip().splitlines():
|
|
56
|
+
parts = line.split()
|
|
57
|
+
if not parts:
|
|
58
|
+
continue
|
|
59
|
+
name = parts[0]
|
|
60
|
+
modules[name] = {
|
|
61
|
+
"parameters": {},
|
|
62
|
+
}
|
|
63
|
+
return modules
|
|
64
|
+
|
|
65
|
+
def _parse_modinfo(self, output: str) -> Optional[ModuleInfo]:
|
|
66
|
+
"""Parse modinfo command output into structured ModuleInfo
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
output (str): modinfo command output
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Optional[ModuleInfo]: parsed module information or None if parsing fails
|
|
73
|
+
"""
|
|
74
|
+
if not output or not output.strip():
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
module_info = ModuleInfo()
|
|
78
|
+
|
|
79
|
+
for line in output.splitlines():
|
|
80
|
+
line = line.strip()
|
|
81
|
+
if not line or ":" not in line:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
field, _, value = line.partition(":")
|
|
85
|
+
field = field.strip()
|
|
86
|
+
value = value.strip()
|
|
87
|
+
|
|
88
|
+
if field == "filename":
|
|
89
|
+
module_info.filename = value
|
|
90
|
+
elif field == "version":
|
|
91
|
+
module_info.version = value
|
|
92
|
+
elif field == "license":
|
|
93
|
+
module_info.license = value
|
|
94
|
+
elif field == "description":
|
|
95
|
+
module_info.description = value
|
|
96
|
+
elif field == "author":
|
|
97
|
+
module_info.author.append(value)
|
|
98
|
+
elif field == "firmware":
|
|
99
|
+
module_info.firmware.append(value)
|
|
100
|
+
elif field == "srcversion":
|
|
101
|
+
module_info.srcversion = value
|
|
102
|
+
elif field == "depends":
|
|
103
|
+
if value:
|
|
104
|
+
module_info.depends = [dep.strip() for dep in value.split(",") if dep.strip()]
|
|
105
|
+
elif field == "name":
|
|
106
|
+
module_info.name = value
|
|
107
|
+
elif field == "vermagic":
|
|
108
|
+
module_info.vermagic = value
|
|
109
|
+
elif field == "sig_id":
|
|
110
|
+
module_info.sig_id = value
|
|
111
|
+
elif field == "signer":
|
|
112
|
+
module_info.signer = value
|
|
113
|
+
elif field == "sig_key":
|
|
114
|
+
module_info.sig_key = value
|
|
115
|
+
elif field == "sig_hashalgo":
|
|
116
|
+
module_info.sig_hashalgo = value
|
|
117
|
+
elif field == "parm":
|
|
118
|
+
param_name, param_desc = value.split(":", 1) if ":" in value else (value, "")
|
|
119
|
+
param_name = param_name.strip()
|
|
120
|
+
param_desc = param_desc.strip()
|
|
121
|
+
|
|
122
|
+
param_type = None
|
|
123
|
+
if param_desc and "(" in param_desc and ")" in param_desc:
|
|
124
|
+
type_start = param_desc.rfind("(")
|
|
125
|
+
type_end = param_desc.rfind(")")
|
|
126
|
+
if type_start < type_end:
|
|
127
|
+
param_type = param_desc[type_start + 1 : type_end].strip()
|
|
128
|
+
param_desc = param_desc[:type_start].strip()
|
|
129
|
+
|
|
130
|
+
module_info.parm.append(
|
|
131
|
+
ModuleParameter(name=param_name, type=param_type, description=param_desc)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return module_info
|
|
135
|
+
|
|
136
|
+
def get_module_parameters(self, module_name: str) -> dict:
|
|
137
|
+
"""Fetches parameter names and values for a given kernel module using _run_sut_cmd
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
module_name (str): name of module to fetch params for
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
dict: param dict of module
|
|
144
|
+
"""
|
|
145
|
+
param_dict = {}
|
|
146
|
+
param_dir = f"/sys/module/{module_name}/parameters"
|
|
147
|
+
|
|
148
|
+
list_params_cmd = f"ls {param_dir}"
|
|
149
|
+
res = self._run_sut_cmd(list_params_cmd)
|
|
150
|
+
if res.exit_code != 0:
|
|
151
|
+
return param_dict
|
|
152
|
+
|
|
153
|
+
for param in res.stdout.strip().splitlines():
|
|
154
|
+
param_path = f"{param_dir}/{param}"
|
|
155
|
+
value_res = self._run_sut_cmd(f"cat {param_path}")
|
|
156
|
+
value = value_res.stdout.strip() if value_res.exit_code == 0 else "<unreadable>"
|
|
157
|
+
param_dict[param] = value
|
|
158
|
+
|
|
159
|
+
return param_dict
|
|
160
|
+
|
|
161
|
+
def collect_all_module_info(self) -> tuple[dict, CommandArtifact]:
|
|
162
|
+
"""Get all modules and its associated params and values
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
RuntimeError: error for failing to get modules
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
tuple[dict, CommandArtifact]: modules found and exit code
|
|
169
|
+
"""
|
|
170
|
+
modules = {}
|
|
171
|
+
res = self._run_sut_cmd(self.CMD)
|
|
172
|
+
if res.exit_code != 0:
|
|
173
|
+
self._log_event(
|
|
174
|
+
category=EventCategory.OS,
|
|
175
|
+
description="Failed to read /proc/modules",
|
|
176
|
+
data={"command": res.command, "exit_code": res.exit_code},
|
|
177
|
+
priority=EventPriority.ERROR,
|
|
178
|
+
console_log=True,
|
|
179
|
+
)
|
|
180
|
+
return modules
|
|
181
|
+
|
|
182
|
+
modules = self.parse_proc_modules(res.stdout)
|
|
183
|
+
|
|
184
|
+
for mod in modules:
|
|
185
|
+
modules[mod]["parameters"] = self.get_module_parameters(mod)
|
|
186
|
+
|
|
187
|
+
if not modules:
|
|
188
|
+
self._log_event(
|
|
189
|
+
category=EventCategory.OS,
|
|
190
|
+
description="Error checking kernel modules",
|
|
191
|
+
data={"command": res.command, "exit_code": res.exit_code},
|
|
192
|
+
priority=EventPriority.ERROR,
|
|
193
|
+
console_log=True,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return modules
|
|
197
|
+
|
|
198
|
+
def collect_data(self, args=None) -> tuple[TaskResult, Optional[KernelModuleDataModel]]:
|
|
199
|
+
"""
|
|
200
|
+
Collect kernel modules data.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
tuple[TaskResult, Optional[KernelModuleDataModel]]: tuple containing the task result and kernel data model or None if not found.
|
|
204
|
+
"""
|
|
205
|
+
kernel_modules = {}
|
|
206
|
+
km_data: KernelModuleDataModel | None = None
|
|
207
|
+
if self.system_info.os_family == OSFamily.WINDOWS:
|
|
208
|
+
res = self._run_sut_cmd(self.CMD_WINDOWS)
|
|
209
|
+
if res.exit_code == 0:
|
|
210
|
+
for line in res.stdout.splitlines():
|
|
211
|
+
if line.startswith("Version="):
|
|
212
|
+
version = line.split("=", 1)[1]
|
|
213
|
+
kernel_modules = {version: {"parameters": {}}}
|
|
214
|
+
break
|
|
215
|
+
|
|
216
|
+
else:
|
|
217
|
+
kernel_modules = self.collect_all_module_info()
|
|
218
|
+
|
|
219
|
+
amdgpu_modinfo = None
|
|
220
|
+
if self.system_info.os_family != OSFamily.WINDOWS:
|
|
221
|
+
# Collect and parse modinfo amdgpu output
|
|
222
|
+
modinfo_res = self._run_sut_cmd(self.CMD_MODINFO_AMDGPU)
|
|
223
|
+
if modinfo_res.exit_code == 0 and modinfo_res.stdout:
|
|
224
|
+
amdgpu_modinfo = self._parse_modinfo(modinfo_res.stdout)
|
|
225
|
+
if amdgpu_modinfo:
|
|
226
|
+
self.result.artifacts.append(
|
|
227
|
+
TextFileArtifact(filename="modinfo_amdgpu.txt", contents=modinfo_res.stdout)
|
|
228
|
+
)
|
|
229
|
+
else:
|
|
230
|
+
self._log_event(
|
|
231
|
+
category=EventCategory.OS,
|
|
232
|
+
description="Could not parse modinfo amdgpu output",
|
|
233
|
+
data={"command": modinfo_res.command},
|
|
234
|
+
priority=EventPriority.WARNING,
|
|
235
|
+
)
|
|
236
|
+
else:
|
|
237
|
+
self._log_event(
|
|
238
|
+
category=EventCategory.OS,
|
|
239
|
+
description="Could not collect modinfo amdgpu output",
|
|
240
|
+
data={
|
|
241
|
+
"command": modinfo_res.command,
|
|
242
|
+
"exit_code": modinfo_res.exit_code,
|
|
243
|
+
"stderr": modinfo_res.stderr,
|
|
244
|
+
},
|
|
245
|
+
priority=EventPriority.WARNING,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if kernel_modules:
|
|
249
|
+
km_data = KernelModuleDataModel(
|
|
250
|
+
kernel_modules=kernel_modules, amdgpu_modinfo=amdgpu_modinfo
|
|
251
|
+
)
|
|
252
|
+
self._log_event(
|
|
253
|
+
category="KERNEL_READ",
|
|
254
|
+
description="Kernel modules read",
|
|
255
|
+
data=km_data.model_dump(),
|
|
256
|
+
priority=EventPriority.INFO,
|
|
257
|
+
)
|
|
258
|
+
self.result.message = f"{len(km_data.kernel_modules)} kernel modules collected"
|
|
259
|
+
self.result.status = ExecutionStatus.OK
|
|
260
|
+
else:
|
|
261
|
+
self.result.message = "Kernel modules not found"
|
|
262
|
+
self.result.status = ExecutionStatus.ERROR
|
|
263
|
+
|
|
264
|
+
return self.result, km_data
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
|
|
27
|
+
from typing import Optional
|
|
28
|
+
|
|
29
|
+
from pydantic import BaseModel, Field
|
|
30
|
+
|
|
31
|
+
from nodescraper.models import DataModel
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ModuleParameter(BaseModel):
|
|
35
|
+
name: str
|
|
36
|
+
type: Optional[str] = None
|
|
37
|
+
description: Optional[str] = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ModuleInfo(BaseModel):
|
|
41
|
+
filename: Optional[str] = None
|
|
42
|
+
version: Optional[str] = None
|
|
43
|
+
license: Optional[str] = None
|
|
44
|
+
description: Optional[str] = None
|
|
45
|
+
author: list[str] = Field(default_factory=list)
|
|
46
|
+
firmware: list[str] = Field(default_factory=list)
|
|
47
|
+
srcversion: Optional[str] = None
|
|
48
|
+
depends: list[str] = Field(default_factory=list)
|
|
49
|
+
name: Optional[str] = None
|
|
50
|
+
vermagic: Optional[str] = None
|
|
51
|
+
sig_id: Optional[str] = None
|
|
52
|
+
signer: Optional[str] = None
|
|
53
|
+
sig_key: Optional[str] = None
|
|
54
|
+
sig_hashalgo: Optional[str] = None
|
|
55
|
+
parm: list[ModuleParameter] = Field(default_factory=list)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class KernelModuleDataModel(DataModel):
|
|
59
|
+
kernel_modules: dict
|
|
60
|
+
amdgpu_modinfo: Optional[ModuleInfo] = None
|
|
@@ -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 KernelModuleAnalyzerArgs
|
|
29
|
+
from .kernel_module_analyzer import KernelModuleAnalyzer
|
|
30
|
+
from .kernel_module_collector import KernelModuleCollector
|
|
31
|
+
from .kernel_module_data import KernelModuleDataModel
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class KernelModulePlugin(InBandDataPlugin[KernelModuleDataModel, None, KernelModuleAnalyzerArgs]):
|
|
35
|
+
"""Plugin for collection and analysis of kernel data"""
|
|
36
|
+
|
|
37
|
+
DATA_MODEL = KernelModuleDataModel
|
|
38
|
+
|
|
39
|
+
COLLECTOR = KernelModuleCollector
|
|
40
|
+
|
|
41
|
+
ANALYZER = KernelModuleAnalyzer
|
|
42
|
+
|
|
43
|
+
ANALYZER_ARGS = KernelModuleAnalyzerArgs
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
###############################################################################
|
|
@@ -0,0 +1,45 @@
|
|
|
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.analyzerargs import AnalyzerArgs
|
|
27
|
+
|
|
28
|
+
from .memorydata import MemoryDataModel
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MemoryAnalyzerArgs(AnalyzerArgs):
|
|
32
|
+
ratio: float = 0.66
|
|
33
|
+
memory_threshold: str = "30Gi"
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def build_from_model(cls, datamodel: MemoryDataModel) -> "MemoryAnalyzerArgs":
|
|
37
|
+
"""build analyzer args from data model
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
datamodel (MemoryDataModel): data model for plugin
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
MemoryAnalyzerArgs: instance of analyzer args class
|
|
44
|
+
"""
|
|
45
|
+
return cls(memory_threshold=datamodel.mem_total)
|
|
@@ -0,0 +1,98 @@
|
|
|
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 nodescraper.enums import EventCategory, EventPriority, ExecutionStatus
|
|
29
|
+
from nodescraper.interfaces import DataAnalyzer
|
|
30
|
+
from nodescraper.models import TaskResult
|
|
31
|
+
from nodescraper.utils import convert_to_bytes
|
|
32
|
+
|
|
33
|
+
from .analyzer_args import MemoryAnalyzerArgs
|
|
34
|
+
from .memorydata import MemoryDataModel
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MemoryAnalyzer(DataAnalyzer[MemoryDataModel, MemoryAnalyzerArgs]):
|
|
38
|
+
"""Check memory usage is within the maximum allowed used memory"""
|
|
39
|
+
|
|
40
|
+
DATA_MODEL = MemoryDataModel
|
|
41
|
+
|
|
42
|
+
def analyze_data(
|
|
43
|
+
self, data: MemoryDataModel, args: Optional[MemoryAnalyzerArgs] = None
|
|
44
|
+
) -> TaskResult:
|
|
45
|
+
"""Analyze the memory data to check if the memory usage is within the maximum allowed used memory.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
data (MemoryDataModel): memory data to analyze.
|
|
49
|
+
args (Optional[MemoryAnalyzerArgs], optional): memory analysis arguments. Defaults to None.
|
|
50
|
+
Returns:
|
|
51
|
+
TaskResult: Result of the memory analysis containing the status and message.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
if not args:
|
|
55
|
+
args = MemoryAnalyzerArgs()
|
|
56
|
+
|
|
57
|
+
def _bytes_to_gb(n: float) -> float:
|
|
58
|
+
return n / (1024**3)
|
|
59
|
+
|
|
60
|
+
free_memory = convert_to_bytes(data.mem_free)
|
|
61
|
+
total_memory = convert_to_bytes(data.mem_total)
|
|
62
|
+
used_memory = total_memory - free_memory
|
|
63
|
+
|
|
64
|
+
threshold_bytes = convert_to_bytes(args.memory_threshold)
|
|
65
|
+
|
|
66
|
+
if total_memory > threshold_bytes:
|
|
67
|
+
base_bytes = threshold_bytes
|
|
68
|
+
base_source = "memory_threshold (max_expected)"
|
|
69
|
+
else:
|
|
70
|
+
base_bytes = total_memory
|
|
71
|
+
base_source = "total_memory"
|
|
72
|
+
|
|
73
|
+
max_allowed_used_mem = base_bytes * args.ratio
|
|
74
|
+
|
|
75
|
+
used_gb = _bytes_to_gb(used_memory)
|
|
76
|
+
allowed_gb = _bytes_to_gb(max_allowed_used_mem)
|
|
77
|
+
base_gb = _bytes_to_gb(base_bytes)
|
|
78
|
+
|
|
79
|
+
if used_memory < max_allowed_used_mem:
|
|
80
|
+
self.result.message = (
|
|
81
|
+
f"Memory usage is within limit: Used {used_gb:.2f} GB "
|
|
82
|
+
f"(allowed {allowed_gb:.2f} GB; base={base_source} {base_gb:.2f} GB × ratio={args.ratio:.2f})"
|
|
83
|
+
)
|
|
84
|
+
self.result.status = ExecutionStatus.OK
|
|
85
|
+
else:
|
|
86
|
+
self.result.message = (
|
|
87
|
+
f"Memory usage exceeded max allowed! Used {used_gb:.2f} GB, "
|
|
88
|
+
f"max allowed {allowed_gb:.2f} GB "
|
|
89
|
+
f"(base={base_source} {base_gb:.2f} GB × ratio={args.ratio:.2f})"
|
|
90
|
+
)
|
|
91
|
+
self.result.status = ExecutionStatus.ERROR
|
|
92
|
+
self._log_event(
|
|
93
|
+
category=EventCategory.OS,
|
|
94
|
+
description=self.result.message,
|
|
95
|
+
priority=EventPriority.CRITICAL,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return self.result
|