amd-node-scraper 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. amd_node_scraper-0.0.1.dist-info/LICENSE +21 -0
  2. amd_node_scraper-0.0.1.dist-info/METADATA +424 -0
  3. amd_node_scraper-0.0.1.dist-info/RECORD +197 -0
  4. amd_node_scraper-0.0.1.dist-info/WHEEL +5 -0
  5. amd_node_scraper-0.0.1.dist-info/entry_points.txt +2 -0
  6. amd_node_scraper-0.0.1.dist-info/top_level.txt +1 -0
  7. nodescraper/__init__.py +32 -0
  8. nodescraper/base/__init__.py +34 -0
  9. nodescraper/base/inbandcollectortask.py +118 -0
  10. nodescraper/base/inbanddataplugin.py +39 -0
  11. nodescraper/base/regexanalyzer.py +120 -0
  12. nodescraper/cli/__init__.py +29 -0
  13. nodescraper/cli/cli.py +511 -0
  14. nodescraper/cli/constants.py +27 -0
  15. nodescraper/cli/dynamicparserbuilder.py +171 -0
  16. nodescraper/cli/helper.py +517 -0
  17. nodescraper/cli/inputargtypes.py +129 -0
  18. nodescraper/configbuilder.py +123 -0
  19. nodescraper/configregistry.py +66 -0
  20. nodescraper/configs/node_status.json +19 -0
  21. nodescraper/connection/__init__.py +25 -0
  22. nodescraper/connection/inband/__init__.py +46 -0
  23. nodescraper/connection/inband/inband.py +171 -0
  24. nodescraper/connection/inband/inbandlocal.py +93 -0
  25. nodescraper/connection/inband/inbandmanager.py +151 -0
  26. nodescraper/connection/inband/inbandremote.py +173 -0
  27. nodescraper/connection/inband/sshparams.py +43 -0
  28. nodescraper/constants.py +26 -0
  29. nodescraper/enums/__init__.py +40 -0
  30. nodescraper/enums/eventcategory.py +89 -0
  31. nodescraper/enums/eventpriority.py +42 -0
  32. nodescraper/enums/executionstatus.py +44 -0
  33. nodescraper/enums/osfamily.py +34 -0
  34. nodescraper/enums/systeminteraction.py +41 -0
  35. nodescraper/enums/systemlocation.py +33 -0
  36. nodescraper/generictypes.py +36 -0
  37. nodescraper/interfaces/__init__.py +44 -0
  38. nodescraper/interfaces/connectionmanager.py +143 -0
  39. nodescraper/interfaces/dataanalyzertask.py +138 -0
  40. nodescraper/interfaces/datacollectortask.py +185 -0
  41. nodescraper/interfaces/dataplugin.py +356 -0
  42. nodescraper/interfaces/plugin.py +127 -0
  43. nodescraper/interfaces/resultcollator.py +56 -0
  44. nodescraper/interfaces/task.py +164 -0
  45. nodescraper/interfaces/taskresulthook.py +39 -0
  46. nodescraper/models/__init__.py +48 -0
  47. nodescraper/models/analyzerargs.py +93 -0
  48. nodescraper/models/collectorargs.py +30 -0
  49. nodescraper/models/connectionconfig.py +34 -0
  50. nodescraper/models/datamodel.py +171 -0
  51. nodescraper/models/datapluginresult.py +39 -0
  52. nodescraper/models/event.py +158 -0
  53. nodescraper/models/pluginconfig.py +38 -0
  54. nodescraper/models/pluginresult.py +39 -0
  55. nodescraper/models/systeminfo.py +44 -0
  56. nodescraper/models/taskresult.py +185 -0
  57. nodescraper/models/timerangeargs.py +38 -0
  58. nodescraper/pluginexecutor.py +274 -0
  59. nodescraper/pluginregistry.py +152 -0
  60. nodescraper/plugins/__init__.py +25 -0
  61. nodescraper/plugins/inband/__init__.py +25 -0
  62. nodescraper/plugins/inband/amdsmi/__init__.py +28 -0
  63. nodescraper/plugins/inband/amdsmi/amdsmi_analyzer.py +821 -0
  64. nodescraper/plugins/inband/amdsmi/amdsmi_collector.py +1313 -0
  65. nodescraper/plugins/inband/amdsmi/amdsmi_plugin.py +43 -0
  66. nodescraper/plugins/inband/amdsmi/amdsmidata.py +1002 -0
  67. nodescraper/plugins/inband/amdsmi/analyzer_args.py +50 -0
  68. nodescraper/plugins/inband/amdsmi/cper.py +65 -0
  69. nodescraper/plugins/inband/bios/__init__.py +29 -0
  70. nodescraper/plugins/inband/bios/analyzer_args.py +64 -0
  71. nodescraper/plugins/inband/bios/bios_analyzer.py +93 -0
  72. nodescraper/plugins/inband/bios/bios_collector.py +93 -0
  73. nodescraper/plugins/inband/bios/bios_plugin.py +43 -0
  74. nodescraper/plugins/inband/bios/biosdata.py +30 -0
  75. nodescraper/plugins/inband/cmdline/__init__.py +25 -0
  76. nodescraper/plugins/inband/cmdline/analyzer_args.py +80 -0
  77. nodescraper/plugins/inband/cmdline/cmdline_analyzer.py +113 -0
  78. nodescraper/plugins/inband/cmdline/cmdline_collector.py +77 -0
  79. nodescraper/plugins/inband/cmdline/cmdline_plugin.py +43 -0
  80. nodescraper/plugins/inband/cmdline/cmdlinedata.py +30 -0
  81. nodescraper/plugins/inband/device_enumeration/__init__.py +29 -0
  82. nodescraper/plugins/inband/device_enumeration/analyzer_args.py +73 -0
  83. nodescraper/plugins/inband/device_enumeration/device_enumeration_analyzer.py +81 -0
  84. nodescraper/plugins/inband/device_enumeration/device_enumeration_collector.py +176 -0
  85. nodescraper/plugins/inband/device_enumeration/device_enumeration_plugin.py +45 -0
  86. nodescraper/plugins/inband/device_enumeration/deviceenumdata.py +36 -0
  87. nodescraper/plugins/inband/dimm/__init__.py +25 -0
  88. nodescraper/plugins/inband/dimm/collector_args.py +31 -0
  89. nodescraper/plugins/inband/dimm/dimm_collector.py +151 -0
  90. nodescraper/plugins/inband/dimm/dimm_plugin.py +40 -0
  91. nodescraper/plugins/inband/dimm/dimmdata.py +30 -0
  92. nodescraper/plugins/inband/dkms/__init__.py +25 -0
  93. nodescraper/plugins/inband/dkms/analyzer_args.py +85 -0
  94. nodescraper/plugins/inband/dkms/dkms_analyzer.py +106 -0
  95. nodescraper/plugins/inband/dkms/dkms_collector.py +76 -0
  96. nodescraper/plugins/inband/dkms/dkms_plugin.py +43 -0
  97. nodescraper/plugins/inband/dkms/dkmsdata.py +33 -0
  98. nodescraper/plugins/inband/dmesg/__init__.py +28 -0
  99. nodescraper/plugins/inband/dmesg/analyzer_args.py +33 -0
  100. nodescraper/plugins/inband/dmesg/collector_args.py +39 -0
  101. nodescraper/plugins/inband/dmesg/dmesg_analyzer.py +503 -0
  102. nodescraper/plugins/inband/dmesg/dmesg_collector.py +164 -0
  103. nodescraper/plugins/inband/dmesg/dmesg_plugin.py +44 -0
  104. nodescraper/plugins/inband/dmesg/dmesgdata.py +116 -0
  105. nodescraper/plugins/inband/fabrics/__init__.py +28 -0
  106. nodescraper/plugins/inband/fabrics/fabrics_collector.py +726 -0
  107. nodescraper/plugins/inband/fabrics/fabrics_plugin.py +37 -0
  108. nodescraper/plugins/inband/fabrics/fabricsdata.py +140 -0
  109. nodescraper/plugins/inband/journal/__init__.py +28 -0
  110. nodescraper/plugins/inband/journal/collector_args.py +33 -0
  111. nodescraper/plugins/inband/journal/journal_collector.py +107 -0
  112. nodescraper/plugins/inband/journal/journal_plugin.py +40 -0
  113. nodescraper/plugins/inband/journal/journaldata.py +44 -0
  114. nodescraper/plugins/inband/kernel/__init__.py +25 -0
  115. nodescraper/plugins/inband/kernel/analyzer_args.py +64 -0
  116. nodescraper/plugins/inband/kernel/kernel_analyzer.py +91 -0
  117. nodescraper/plugins/inband/kernel/kernel_collector.py +129 -0
  118. nodescraper/plugins/inband/kernel/kernel_plugin.py +43 -0
  119. nodescraper/plugins/inband/kernel/kerneldata.py +32 -0
  120. nodescraper/plugins/inband/kernel_module/__init__.py +25 -0
  121. nodescraper/plugins/inband/kernel_module/analyzer_args.py +59 -0
  122. nodescraper/plugins/inband/kernel_module/kernel_module_analyzer.py +211 -0
  123. nodescraper/plugins/inband/kernel_module/kernel_module_collector.py +264 -0
  124. nodescraper/plugins/inband/kernel_module/kernel_module_data.py +60 -0
  125. nodescraper/plugins/inband/kernel_module/kernel_module_plugin.py +43 -0
  126. nodescraper/plugins/inband/memory/__init__.py +25 -0
  127. nodescraper/plugins/inband/memory/analyzer_args.py +45 -0
  128. nodescraper/plugins/inband/memory/memory_analyzer.py +98 -0
  129. nodescraper/plugins/inband/memory/memory_collector.py +330 -0
  130. nodescraper/plugins/inband/memory/memory_plugin.py +43 -0
  131. nodescraper/plugins/inband/memory/memorydata.py +90 -0
  132. nodescraper/plugins/inband/network/__init__.py +28 -0
  133. nodescraper/plugins/inband/network/network_collector.py +1828 -0
  134. nodescraper/plugins/inband/network/network_plugin.py +37 -0
  135. nodescraper/plugins/inband/network/networkdata.py +319 -0
  136. nodescraper/plugins/inband/nvme/__init__.py +28 -0
  137. nodescraper/plugins/inband/nvme/nvme_collector.py +167 -0
  138. nodescraper/plugins/inband/nvme/nvme_plugin.py +37 -0
  139. nodescraper/plugins/inband/nvme/nvmedata.py +45 -0
  140. nodescraper/plugins/inband/os/__init__.py +25 -0
  141. nodescraper/plugins/inband/os/analyzer_args.py +64 -0
  142. nodescraper/plugins/inband/os/os_analyzer.py +73 -0
  143. nodescraper/plugins/inband/os/os_collector.py +131 -0
  144. nodescraper/plugins/inband/os/os_plugin.py +43 -0
  145. nodescraper/plugins/inband/os/osdata.py +31 -0
  146. nodescraper/plugins/inband/package/__init__.py +25 -0
  147. nodescraper/plugins/inband/package/analyzer_args.py +48 -0
  148. nodescraper/plugins/inband/package/package_analyzer.py +253 -0
  149. nodescraper/plugins/inband/package/package_collector.py +273 -0
  150. nodescraper/plugins/inband/package/package_plugin.py +43 -0
  151. nodescraper/plugins/inband/package/packagedata.py +41 -0
  152. nodescraper/plugins/inband/pcie/__init__.py +29 -0
  153. nodescraper/plugins/inband/pcie/analyzer_args.py +63 -0
  154. nodescraper/plugins/inband/pcie/pcie_analyzer.py +1081 -0
  155. nodescraper/plugins/inband/pcie/pcie_collector.py +690 -0
  156. nodescraper/plugins/inband/pcie/pcie_data.py +2017 -0
  157. nodescraper/plugins/inband/pcie/pcie_plugin.py +43 -0
  158. nodescraper/plugins/inband/process/__init__.py +25 -0
  159. nodescraper/plugins/inband/process/analyzer_args.py +45 -0
  160. nodescraper/plugins/inband/process/collector_args.py +31 -0
  161. nodescraper/plugins/inband/process/process_analyzer.py +91 -0
  162. nodescraper/plugins/inband/process/process_collector.py +115 -0
  163. nodescraper/plugins/inband/process/process_plugin.py +46 -0
  164. nodescraper/plugins/inband/process/processdata.py +34 -0
  165. nodescraper/plugins/inband/rocm/__init__.py +25 -0
  166. nodescraper/plugins/inband/rocm/analyzer_args.py +66 -0
  167. nodescraper/plugins/inband/rocm/rocm_analyzer.py +100 -0
  168. nodescraper/plugins/inband/rocm/rocm_collector.py +205 -0
  169. nodescraper/plugins/inband/rocm/rocm_plugin.py +43 -0
  170. nodescraper/plugins/inband/rocm/rocmdata.py +62 -0
  171. nodescraper/plugins/inband/storage/__init__.py +25 -0
  172. nodescraper/plugins/inband/storage/analyzer_args.py +38 -0
  173. nodescraper/plugins/inband/storage/collector_args.py +31 -0
  174. nodescraper/plugins/inband/storage/storage_analyzer.py +152 -0
  175. nodescraper/plugins/inband/storage/storage_collector.py +110 -0
  176. nodescraper/plugins/inband/storage/storage_plugin.py +44 -0
  177. nodescraper/plugins/inband/storage/storagedata.py +70 -0
  178. nodescraper/plugins/inband/sysctl/__init__.py +29 -0
  179. nodescraper/plugins/inband/sysctl/analyzer_args.py +67 -0
  180. nodescraper/plugins/inband/sysctl/sysctl_analyzer.py +81 -0
  181. nodescraper/plugins/inband/sysctl/sysctl_collector.py +101 -0
  182. nodescraper/plugins/inband/sysctl/sysctl_plugin.py +43 -0
  183. nodescraper/plugins/inband/sysctl/sysctldata.py +42 -0
  184. nodescraper/plugins/inband/syslog/__init__.py +28 -0
  185. nodescraper/plugins/inband/syslog/syslog_collector.py +121 -0
  186. nodescraper/plugins/inband/syslog/syslog_plugin.py +37 -0
  187. nodescraper/plugins/inband/syslog/syslogdata.py +46 -0
  188. nodescraper/plugins/inband/uptime/__init__.py +25 -0
  189. nodescraper/plugins/inband/uptime/uptime_collector.py +88 -0
  190. nodescraper/plugins/inband/uptime/uptime_plugin.py +37 -0
  191. nodescraper/plugins/inband/uptime/uptimedata.py +31 -0
  192. nodescraper/resultcollators/__init__.py +25 -0
  193. nodescraper/resultcollators/tablesummary.py +159 -0
  194. nodescraper/taskresulthooks/__init__.py +28 -0
  195. nodescraper/taskresulthooks/filesystemloghook.py +88 -0
  196. nodescraper/typeutils.py +171 -0
  197. nodescraper/utils.py +412 -0
@@ -0,0 +1,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