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,113 @@
|
|
|
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
|
+
|
|
32
|
+
from .analyzer_args import CmdlineAnalyzerArgs
|
|
33
|
+
from .cmdlinedata import CmdlineDataModel
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CmdlineAnalyzer(DataAnalyzer[CmdlineDataModel, CmdlineAnalyzerArgs]):
|
|
37
|
+
"""Check cmdline matches expected kernel cmdline"""
|
|
38
|
+
|
|
39
|
+
DATA_MODEL = CmdlineDataModel
|
|
40
|
+
|
|
41
|
+
def _compare_cmdline(self, cmdline: str, required_cmdline: list, banned_cmdline: list) -> bool:
|
|
42
|
+
"""Compare the kernel cmdline against required and banned cmdline arguments.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
cmdline (str): Kernel command line arguments as a string.
|
|
46
|
+
required_cmdline (list): required kernel cmdline arguments that must be present.
|
|
47
|
+
banned_cmdline (list): banned kernel cmdline arguments that must not be present.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
bool: True if the cmdline matches the required arguments and does not contain banned arguments,
|
|
51
|
+
False otherwise.
|
|
52
|
+
"""
|
|
53
|
+
# Check for missing required arguments
|
|
54
|
+
missing_required = [arg for arg in required_cmdline if arg not in cmdline]
|
|
55
|
+
found_banned = [arg for arg in banned_cmdline if arg in cmdline]
|
|
56
|
+
|
|
57
|
+
if len(missing_required) >= 1:
|
|
58
|
+
self._log_event(
|
|
59
|
+
category=EventCategory.OS,
|
|
60
|
+
description=f"Missing {len(missing_required)} required kernel cmdline arguments",
|
|
61
|
+
priority=EventPriority.ERROR,
|
|
62
|
+
data={"missing_required": missing_required},
|
|
63
|
+
console_log=True,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if len(found_banned) >= 1:
|
|
67
|
+
self._log_event(
|
|
68
|
+
category=EventCategory.OS,
|
|
69
|
+
description=f"Found {len(found_banned)} banned kernel cmdline arguments",
|
|
70
|
+
priority=EventPriority.ERROR,
|
|
71
|
+
data={"found_banned": found_banned},
|
|
72
|
+
console_log=True,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return not (missing_required or found_banned), missing_required, found_banned
|
|
76
|
+
|
|
77
|
+
def analyze_data(
|
|
78
|
+
self, data: CmdlineDataModel, args: Optional[CmdlineAnalyzerArgs] = None
|
|
79
|
+
) -> TaskResult:
|
|
80
|
+
"""Analyze the cmdline data against the provided arguments.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
data (CmdlineDataModel): Cmdline data model containing the kernel command line.
|
|
84
|
+
args (Optional[CmdlineAnalyzerArgs], optional): Cmdline analysis arguments. Defaults to None.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
TaskResult: Result of the cmdline analysis containing status and message.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
if not args:
|
|
91
|
+
self.result.message = "Cmdline analysis args not provided"
|
|
92
|
+
self.result.status = ExecutionStatus.NOT_RAN
|
|
93
|
+
return self.result
|
|
94
|
+
|
|
95
|
+
# check if any of the cmdline defined in the list match the actual kernel cmdline
|
|
96
|
+
check, missing_required, found_banned = self._compare_cmdline(
|
|
97
|
+
data.cmdline, args.required_cmdline, args.banned_cmdline
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if check:
|
|
101
|
+
self.result.message = "Kernel cmdline matches expected"
|
|
102
|
+
self.result.status = ExecutionStatus.OK
|
|
103
|
+
return self.result
|
|
104
|
+
|
|
105
|
+
self.result.message = f"Illegal kernel cmdline, found_banned: {found_banned}, missing required: {missing_required}"
|
|
106
|
+
self.result.status = ExecutionStatus.ERROR
|
|
107
|
+
self._log_event(
|
|
108
|
+
category=EventCategory.OS,
|
|
109
|
+
description=self.result.message,
|
|
110
|
+
priority=EventPriority.CRITICAL,
|
|
111
|
+
console_log=True,
|
|
112
|
+
)
|
|
113
|
+
return self.result
|
|
@@ -0,0 +1,77 @@
|
|
|
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.enums import EventCategory, EventPriority, ExecutionStatus, OSFamily
|
|
30
|
+
from nodescraper.models import TaskResult
|
|
31
|
+
|
|
32
|
+
from .cmdlinedata import CmdlineDataModel
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CmdlineCollector(InBandDataCollector[CmdlineDataModel, None]):
|
|
36
|
+
"""Read linux cmdline data"""
|
|
37
|
+
|
|
38
|
+
SUPPORTED_OS_FAMILY = {OSFamily.LINUX}
|
|
39
|
+
|
|
40
|
+
DATA_MODEL = CmdlineDataModel
|
|
41
|
+
|
|
42
|
+
CMD = "cat /proc/cmdline"
|
|
43
|
+
|
|
44
|
+
def collect_data(
|
|
45
|
+
self,
|
|
46
|
+
args=None,
|
|
47
|
+
) -> tuple[TaskResult, Optional[CmdlineDataModel]]:
|
|
48
|
+
"""
|
|
49
|
+
Collects the cmdline data from the system.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
tuple[TaskResult, Optional[CmdlineDataModel]]: tuple containing the task result and the cmdline data model if successful, otherwise None.
|
|
53
|
+
"""
|
|
54
|
+
res = self._run_sut_cmd(self.CMD)
|
|
55
|
+
cmdline_data = None
|
|
56
|
+
if res.exit_code == 0:
|
|
57
|
+
cmdline_data = CmdlineDataModel(cmdline=res.stdout)
|
|
58
|
+
self._log_event(
|
|
59
|
+
category="CMDLINE_READ",
|
|
60
|
+
description="cmdline read",
|
|
61
|
+
data=cmdline_data.model_dump(),
|
|
62
|
+
priority=EventPriority.INFO,
|
|
63
|
+
)
|
|
64
|
+
self.result.message = f"cmdline: {res.stdout}"
|
|
65
|
+
self.result.status = ExecutionStatus.OK
|
|
66
|
+
else:
|
|
67
|
+
self._log_event(
|
|
68
|
+
category=EventCategory.OS,
|
|
69
|
+
description="Error checking cmdline",
|
|
70
|
+
data={"command": res.command, "exit_code": res.exit_code},
|
|
71
|
+
priority=EventPriority.ERROR,
|
|
72
|
+
console_log=True,
|
|
73
|
+
)
|
|
74
|
+
self.result.message = "cmdline not found"
|
|
75
|
+
self.result.status = ExecutionStatus.ERROR
|
|
76
|
+
|
|
77
|
+
return self.result, cmdline_data
|
|
@@ -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 CmdlineAnalyzerArgs
|
|
29
|
+
from .cmdline_analyzer import CmdlineAnalyzer
|
|
30
|
+
from .cmdline_collector import CmdlineCollector
|
|
31
|
+
from .cmdlinedata import CmdlineDataModel
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CmdlinePlugin(InBandDataPlugin[CmdlineDataModel, None, CmdlineAnalyzerArgs]):
|
|
35
|
+
"""Plugin for collection and analysis of linux cmdline data"""
|
|
36
|
+
|
|
37
|
+
DATA_MODEL = CmdlineDataModel
|
|
38
|
+
|
|
39
|
+
COLLECTOR = CmdlineCollector
|
|
40
|
+
|
|
41
|
+
ANALYZER = CmdlineAnalyzer
|
|
42
|
+
|
|
43
|
+
ANALYZER_ARGS = CmdlineAnalyzerArgs
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
#
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
# copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
# SOFTWARE.
|
|
24
|
+
#
|
|
25
|
+
###############################################################################
|
|
26
|
+
from nodescraper.models import DataModel
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CmdlineDataModel(DataModel):
|
|
30
|
+
cmdline: str
|
|
@@ -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 DeviceEnumerationAnalyzerArgs
|
|
27
|
+
from .device_enumeration_plugin import DeviceEnumerationPlugin
|
|
28
|
+
|
|
29
|
+
__all__ = ["DeviceEnumerationPlugin", "DeviceEnumerationAnalyzerArgs"]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
#
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
# copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
# SOFTWARE.
|
|
24
|
+
#
|
|
25
|
+
###############################################################################
|
|
26
|
+
from typing import Any, Optional
|
|
27
|
+
|
|
28
|
+
from pydantic import field_validator
|
|
29
|
+
|
|
30
|
+
from nodescraper.models import AnalyzerArgs
|
|
31
|
+
|
|
32
|
+
from .deviceenumdata import DeviceEnumerationDataModel
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DeviceEnumerationAnalyzerArgs(AnalyzerArgs):
|
|
36
|
+
cpu_count: Optional[list[int]] = None
|
|
37
|
+
gpu_count: Optional[list[int]] = None
|
|
38
|
+
vf_count: Optional[list[int]] = None
|
|
39
|
+
|
|
40
|
+
@field_validator("cpu_count", "gpu_count", "vf_count", mode="before")
|
|
41
|
+
@classmethod
|
|
42
|
+
def normalize_to_list(cls, v: Any) -> Optional[list[int]]:
|
|
43
|
+
"""Convert single integer values to lists for consistent handling.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
v: The input value (can be int, list[int], or None).
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Optional[list[int]]: The normalized list value or None.
|
|
50
|
+
"""
|
|
51
|
+
if v is None:
|
|
52
|
+
return None
|
|
53
|
+
if isinstance(v, int):
|
|
54
|
+
return [v]
|
|
55
|
+
return v
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def build_from_model(
|
|
59
|
+
cls, datamodel: DeviceEnumerationDataModel
|
|
60
|
+
) -> "DeviceEnumerationAnalyzerArgs":
|
|
61
|
+
"""build analyzer args from data model
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
datamodel (DeviceEnumerationDataModel): data model for plugin
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
DeviceEnumerationAnalyzerArgs: instance of analyzer args class
|
|
68
|
+
"""
|
|
69
|
+
return cls(
|
|
70
|
+
cpu_count=[datamodel.cpu_count] if datamodel.cpu_count is not None else None,
|
|
71
|
+
gpu_count=[datamodel.gpu_count] if datamodel.gpu_count is not None else None,
|
|
72
|
+
vf_count=[datamodel.vf_count] if datamodel.vf_count is not None else None,
|
|
73
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
|
|
32
|
+
from .analyzer_args import DeviceEnumerationAnalyzerArgs
|
|
33
|
+
from .deviceenumdata import DeviceEnumerationDataModel
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DeviceEnumerationAnalyzer(
|
|
37
|
+
DataAnalyzer[DeviceEnumerationDataModel, DeviceEnumerationAnalyzerArgs]
|
|
38
|
+
):
|
|
39
|
+
"""Check Device Enumeration matches expected cpu and gpu count
|
|
40
|
+
supported by all OSs, SKUs, and platforms."""
|
|
41
|
+
|
|
42
|
+
DATA_MODEL = DeviceEnumerationDataModel
|
|
43
|
+
|
|
44
|
+
def analyze_data(
|
|
45
|
+
self, data: DeviceEnumerationDataModel, args: Optional[DeviceEnumerationAnalyzerArgs] = None
|
|
46
|
+
) -> TaskResult:
|
|
47
|
+
|
|
48
|
+
if args is None:
|
|
49
|
+
self.result.status = ExecutionStatus.NOT_RAN
|
|
50
|
+
self.result.message = (
|
|
51
|
+
"Expected Device Enumeration data not provided, skipping analysis."
|
|
52
|
+
)
|
|
53
|
+
return self.result
|
|
54
|
+
|
|
55
|
+
checks = {}
|
|
56
|
+
if args.cpu_count is not None and args.cpu_count != []:
|
|
57
|
+
checks["cpu_count"] = args.cpu_count
|
|
58
|
+
if args.gpu_count is not None and args.gpu_count != []:
|
|
59
|
+
checks["gpu_count"] = args.gpu_count
|
|
60
|
+
if args.vf_count is not None and args.vf_count != []:
|
|
61
|
+
checks["vf_count"] = args.vf_count
|
|
62
|
+
|
|
63
|
+
self.result.message = ""
|
|
64
|
+
for check, accepted_counts in checks.items():
|
|
65
|
+
actual_count = getattr(data, check)
|
|
66
|
+
if actual_count not in accepted_counts:
|
|
67
|
+
message = f"Expected {check} in {accepted_counts}, but got {actual_count}. "
|
|
68
|
+
self.result.message += message
|
|
69
|
+
self.result.status = ExecutionStatus.ERROR
|
|
70
|
+
self._log_event(
|
|
71
|
+
category=EventCategory.PLATFORM,
|
|
72
|
+
description=message,
|
|
73
|
+
data={check: actual_count},
|
|
74
|
+
priority=EventPriority.CRITICAL,
|
|
75
|
+
console_log=True,
|
|
76
|
+
)
|
|
77
|
+
if self.result.message == "":
|
|
78
|
+
self.result.status = ExecutionStatus.OK
|
|
79
|
+
self.result.message = f"Device Enumeration validated on {checks.keys()}."
|
|
80
|
+
|
|
81
|
+
return self.result
|
|
@@ -0,0 +1,176 @@
|
|
|
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.inband import CommandArtifact, TextFileArtifact
|
|
30
|
+
from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus, OSFamily
|
|
31
|
+
from nodescraper.models import TaskResult
|
|
32
|
+
|
|
33
|
+
from .deviceenumdata import DeviceEnumerationDataModel
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DeviceEnumerationCollector(InBandDataCollector[DeviceEnumerationDataModel, None]):
|
|
37
|
+
"""Collect CPU and GPU count"""
|
|
38
|
+
|
|
39
|
+
DATA_MODEL = DeviceEnumerationDataModel
|
|
40
|
+
|
|
41
|
+
CMD_GPU_COUNT_LINUX = "lspci -d {vendorid_ep}: | grep -i 'VGA\\|Display\\|3D' | wc -l"
|
|
42
|
+
CMD_VF_COUNT_LINUX = "lspci -d {vendorid_ep}: | grep -i 'Virtual Function' | wc -l"
|
|
43
|
+
CMD_LSCPU_LINUX = "lscpu"
|
|
44
|
+
CMD_LSHW_LINUX = "lshw"
|
|
45
|
+
|
|
46
|
+
CMD_CPU_COUNT_WINDOWS = (
|
|
47
|
+
'powershell -Command "(Get-WmiObject -Class Win32_Processor | Measure-Object).Count"'
|
|
48
|
+
)
|
|
49
|
+
CMD_GPU_COUNT_WINDOWS = 'powershell -Command "(wmic path win32_VideoController get name | findstr AMD | Measure-Object).Count"'
|
|
50
|
+
CMD_VF_COUNT_WINDOWS = (
|
|
51
|
+
'powershell -Command "(Get-VMHostPartitionableGpu | Measure-Object).Count"'
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def _warning(
|
|
55
|
+
self,
|
|
56
|
+
description: str,
|
|
57
|
+
command: CommandArtifact,
|
|
58
|
+
category: EventCategory = EventCategory.PLATFORM,
|
|
59
|
+
):
|
|
60
|
+
self._log_event(
|
|
61
|
+
category=category,
|
|
62
|
+
description=description,
|
|
63
|
+
data={
|
|
64
|
+
"command": command.command,
|
|
65
|
+
"exit_code": command.exit_code,
|
|
66
|
+
"stderr": command.stderr,
|
|
67
|
+
},
|
|
68
|
+
priority=EventPriority.WARNING,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def collect_data(self, args=None) -> tuple[TaskResult, Optional[DeviceEnumerationDataModel]]:
|
|
72
|
+
"""
|
|
73
|
+
Read CPU and GPU count
|
|
74
|
+
On Linux, use lscpu and lspci
|
|
75
|
+
On Windows, use WMI and hyper-v cmdlets
|
|
76
|
+
"""
|
|
77
|
+
if self.system_info.os_family == OSFamily.LINUX:
|
|
78
|
+
lscpu_res = self._run_sut_cmd(self.CMD_LSCPU_LINUX, log_artifact=False)
|
|
79
|
+
|
|
80
|
+
# Count all AMD GPUs
|
|
81
|
+
vendor_id = format(self.system_info.vendorid_ep, "x")
|
|
82
|
+
gpu_count_res = self._run_sut_cmd(
|
|
83
|
+
self.CMD_GPU_COUNT_LINUX.format(vendorid_ep=vendor_id)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Count AMD Virtual Functions
|
|
87
|
+
vf_count_res = self._run_sut_cmd(self.CMD_VF_COUNT_LINUX.format(vendorid_ep=vendor_id))
|
|
88
|
+
|
|
89
|
+
# Collect lshw output
|
|
90
|
+
lshw_res = self._run_sut_cmd(self.CMD_LSHW_LINUX, sudo=True, log_artifact=False)
|
|
91
|
+
else:
|
|
92
|
+
cpu_count_res = self._run_sut_cmd(self.CMD_CPU_COUNT_WINDOWS)
|
|
93
|
+
gpu_count_res = self._run_sut_cmd(self.CMD_GPU_COUNT_WINDOWS)
|
|
94
|
+
vf_count_res = self._run_sut_cmd(self.CMD_VF_COUNT_WINDOWS)
|
|
95
|
+
|
|
96
|
+
device_enum = DeviceEnumerationDataModel()
|
|
97
|
+
|
|
98
|
+
if self.system_info.os_family == OSFamily.LINUX:
|
|
99
|
+
if lscpu_res.exit_code == 0 and lscpu_res.stdout:
|
|
100
|
+
# Extract socket count from lscpu output
|
|
101
|
+
for line in lscpu_res.stdout.splitlines():
|
|
102
|
+
if line.startswith("Socket(s):"):
|
|
103
|
+
try:
|
|
104
|
+
device_enum.cpu_count = int(line.split(":")[1].strip())
|
|
105
|
+
break
|
|
106
|
+
except (ValueError, IndexError):
|
|
107
|
+
self._warning(
|
|
108
|
+
description="Cannot parse CPU count from lscpu output",
|
|
109
|
+
command=lscpu_res,
|
|
110
|
+
)
|
|
111
|
+
device_enum.lscpu_output = lscpu_res.stdout
|
|
112
|
+
self._log_event(
|
|
113
|
+
category=EventCategory.PLATFORM,
|
|
114
|
+
description="Collected lscpu output",
|
|
115
|
+
priority=EventPriority.INFO,
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
self._warning(description="Cannot collect lscpu output", command=lscpu_res)
|
|
119
|
+
else:
|
|
120
|
+
if cpu_count_res.exit_code == 0:
|
|
121
|
+
device_enum.cpu_count = int(cpu_count_res.stdout)
|
|
122
|
+
else:
|
|
123
|
+
self._warning(description="Cannot determine CPU count", command=cpu_count_res)
|
|
124
|
+
|
|
125
|
+
if gpu_count_res.exit_code == 0:
|
|
126
|
+
device_enum.gpu_count = int(gpu_count_res.stdout)
|
|
127
|
+
else:
|
|
128
|
+
self._warning(description="Cannot determine GPU count", command=gpu_count_res)
|
|
129
|
+
|
|
130
|
+
if vf_count_res.exit_code == 0:
|
|
131
|
+
device_enum.vf_count = int(vf_count_res.stdout)
|
|
132
|
+
else:
|
|
133
|
+
self._warning(
|
|
134
|
+
description="Cannot determine VF count",
|
|
135
|
+
command=vf_count_res,
|
|
136
|
+
category=EventCategory.SW_DRIVER,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Collect lshw output on Linux
|
|
140
|
+
if self.system_info.os_family == OSFamily.LINUX:
|
|
141
|
+
if lshw_res.exit_code == 0 and lshw_res.stdout:
|
|
142
|
+
device_enum.lshw_output = lshw_res.stdout
|
|
143
|
+
self.result.artifacts.append(
|
|
144
|
+
TextFileArtifact(filename="lshw.txt", contents=lshw_res.stdout)
|
|
145
|
+
)
|
|
146
|
+
self._log_event(
|
|
147
|
+
category=EventCategory.PLATFORM,
|
|
148
|
+
description="Collected lshw output",
|
|
149
|
+
priority=EventPriority.INFO,
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
self._warning(description="Cannot collect lshw output", command=lshw_res)
|
|
153
|
+
|
|
154
|
+
if device_enum.cpu_count or device_enum.gpu_count or device_enum.vf_count:
|
|
155
|
+
log_data = device_enum.model_dump(
|
|
156
|
+
exclude_none=True,
|
|
157
|
+
exclude={"lscpu_output", "lshw_output", "task_name", "task_type", "parent"},
|
|
158
|
+
)
|
|
159
|
+
self._log_event(
|
|
160
|
+
category=EventCategory.PLATFORM,
|
|
161
|
+
description=f"Counted {device_enum.cpu_count} CPUs, {device_enum.gpu_count} GPUs, {device_enum.vf_count} VFs",
|
|
162
|
+
data=log_data,
|
|
163
|
+
priority=EventPriority.INFO,
|
|
164
|
+
)
|
|
165
|
+
self.result.message = f"Device Enumeration: {log_data}"
|
|
166
|
+
self.result.status = ExecutionStatus.OK
|
|
167
|
+
return self.result, device_enum
|
|
168
|
+
else:
|
|
169
|
+
self.result.message = "Device Enumeration info not found"
|
|
170
|
+
self.result.status = ExecutionStatus.EXECUTION_FAILURE
|
|
171
|
+
self._log_event(
|
|
172
|
+
category=EventCategory.SW_DRIVER,
|
|
173
|
+
description=self.result.message,
|
|
174
|
+
priority=EventPriority.CRITICAL,
|
|
175
|
+
)
|
|
176
|
+
return self.result, None
|
|
@@ -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.base import InBandDataPlugin
|
|
27
|
+
|
|
28
|
+
from .analyzer_args import DeviceEnumerationAnalyzerArgs
|
|
29
|
+
from .device_enumeration_analyzer import DeviceEnumerationAnalyzer
|
|
30
|
+
from .device_enumeration_collector import DeviceEnumerationCollector
|
|
31
|
+
from .deviceenumdata import DeviceEnumerationDataModel
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DeviceEnumerationPlugin(
|
|
35
|
+
InBandDataPlugin[DeviceEnumerationDataModel, None, DeviceEnumerationAnalyzerArgs]
|
|
36
|
+
):
|
|
37
|
+
"""Plugin for collection and analysis of BIOS data"""
|
|
38
|
+
|
|
39
|
+
DATA_MODEL = DeviceEnumerationDataModel
|
|
40
|
+
|
|
41
|
+
COLLECTOR = DeviceEnumerationCollector
|
|
42
|
+
|
|
43
|
+
ANALYZER = DeviceEnumerationAnalyzer
|
|
44
|
+
|
|
45
|
+
ANALYZER_ARGS = DeviceEnumerationAnalyzerArgs
|