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,129 @@
1
+ ###############################################################################
2
+ #
3
+ # MIT License
4
+ #
5
+ # Copyright (c) 2025 Advanced Micro Devices, Inc.
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+ ###############################################################################
26
+ import re
27
+ from typing import Optional
28
+
29
+ from nodescraper.base import InBandDataCollector
30
+ from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus, OSFamily
31
+ from nodescraper.models import TaskResult
32
+
33
+ from .kerneldata import KernelDataModel
34
+
35
+
36
+ class KernelCollector(InBandDataCollector[KernelDataModel, None]):
37
+ """Read kernel version"""
38
+
39
+ DATA_MODEL = KernelDataModel
40
+ CMD_WINDOWS = "wmic os get Version /Value"
41
+ CMD = "sh -c 'uname -a'"
42
+
43
+ def _parse_kernel_version(self, uname_a: str) -> Optional[str]:
44
+ """Extract the kernel release from `uname -a` output.
45
+
46
+ Args:
47
+ uname_a (str): The full output string from the `uname -a` command.
48
+
49
+ Returns:
50
+ Optional[str]: The parsed kernel release (e.g., "5.13.0-30-generic")
51
+ if found, otherwise None.
52
+ """
53
+ if not uname_a:
54
+ return None
55
+
56
+ result = uname_a.strip().split()
57
+ if len(result) >= 3:
58
+ return result[2]
59
+
60
+ # if some change in output look for a version-like string (e.g. 4.18.0-553.el8_10.x86_64)
61
+ match = re.search(r"\d+\.\d+\.\d+[\w\-\.]*", uname_a)
62
+ if match:
63
+ return match.group(0)
64
+
65
+ return None
66
+
67
+ def collect_data(
68
+ self,
69
+ args=None,
70
+ ) -> tuple[TaskResult, Optional[KernelDataModel]]:
71
+ """
72
+ Collect kernel version data.
73
+
74
+ Returns:
75
+ tuple[TaskResult, Optional[KernelDataModel]]: tuple containing the task result and kernel data model or None if not found.
76
+ """
77
+
78
+ kernel = None
79
+ kernel_info = None
80
+
81
+ if self.system_info.os_family == OSFamily.WINDOWS:
82
+ res = self._run_sut_cmd(self.CMD_WINDOWS)
83
+ if res.exit_code == 0:
84
+ kernel_info = res.stdout
85
+ kernel = [line for line in res.stdout.splitlines() if "Version=" in line][0].split(
86
+ "="
87
+ )[1]
88
+ else:
89
+ res = self._run_sut_cmd(self.CMD)
90
+ if res.exit_code == 0:
91
+ kernel_info = res.stdout
92
+ kernel = self._parse_kernel_version(kernel_info)
93
+ if not kernel:
94
+ self._log_event(
95
+ category=EventCategory.OS,
96
+ description="Could not extract kernel version from 'uname -a'",
97
+ data={"command": res.command, "exit_code": res.exit_code},
98
+ priority=EventPriority.ERROR,
99
+ console_log=True,
100
+ )
101
+
102
+ if res.exit_code != 0:
103
+ self._log_event(
104
+ category=EventCategory.OS,
105
+ description="Error checking kernel version",
106
+ data={"command": res.command, "exit_code": res.exit_code},
107
+ priority=EventPriority.ERROR,
108
+ console_log=True,
109
+ )
110
+
111
+ if kernel_info and kernel:
112
+
113
+ kernel_data = KernelDataModel(kernel_info=kernel_info, kernel_version=kernel)
114
+ self._log_event(
115
+ category="KERNEL_READ",
116
+ description="Kernel version read",
117
+ data=kernel_data.model_dump(),
118
+ priority=EventPriority.INFO,
119
+ )
120
+ else:
121
+ kernel_data = None
122
+
123
+ self.result.message = (
124
+ "Kernel not found"
125
+ if not kernel_info
126
+ else f"Kernel info: {kernel_info} | Kernel version: {kernel if kernel else 'Kernel version not found'}"
127
+ )
128
+ self.result.status = ExecutionStatus.OK if kernel_info else ExecutionStatus.ERROR
129
+ return self.result, kernel_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 KernelAnalyzerArgs
29
+ from .kernel_analyzer import KernelAnalyzer
30
+ from .kernel_collector import KernelCollector
31
+ from .kerneldata import KernelDataModel
32
+
33
+
34
+ class KernelPlugin(InBandDataPlugin[KernelDataModel, None, KernelAnalyzerArgs]):
35
+ """Plugin for collection and analysis of kernel data"""
36
+
37
+ DATA_MODEL = KernelDataModel
38
+
39
+ COLLECTOR = KernelCollector
40
+
41
+ ANALYZER = KernelAnalyzer
42
+
43
+ ANALYZER_ARGS = KernelAnalyzerArgs
@@ -0,0 +1,32 @@
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 nodescraper.models import DataModel
28
+
29
+
30
+ class KernelDataModel(DataModel):
31
+ kernel_info: str
32
+ kernel_version: str
@@ -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,59 @@
1
+ ###############################################################################
2
+ #
3
+ # MIT License
4
+ #
5
+ # Copyright (c) 2025 Advanced Micro Devices, Inc.
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+ ###############################################################################
26
+ import re
27
+
28
+ from nodescraper.models import AnalyzerArgs
29
+ from nodescraper.plugins.inband.kernel_module.kernel_module_data import (
30
+ KernelModuleDataModel,
31
+ )
32
+
33
+
34
+ class KernelModuleAnalyzerArgs(AnalyzerArgs):
35
+ kernel_modules: dict[str, dict] = {}
36
+ regex_filter: list[str] = ["amd"]
37
+
38
+ @classmethod
39
+ def build_from_model(cls, datamodel: KernelModuleDataModel) -> "KernelModuleAnalyzerArgs":
40
+ """build analyzer args from data model and filter by regex_filter
41
+
42
+ Args:
43
+ datamodel (KernelModuleDataModel): data model for plugin
44
+
45
+ Returns:
46
+ KernelModuleAnalyzerArgs: instance of analyzer args class
47
+ """
48
+
49
+ pattern_regex = re.compile("amd", re.IGNORECASE)
50
+ filtered_mods = {
51
+ name: data
52
+ for name, data in datamodel.kernel_modules.items()
53
+ if pattern_regex.search(name)
54
+ }
55
+
56
+ return cls(
57
+ kernel_modules=filtered_mods,
58
+ regex_filter=[],
59
+ )
@@ -0,0 +1,211 @@
1
+ ###############################################################################
2
+ #
3
+ # MIT License
4
+ #
5
+ # Copyright (c) 2025 Advanced Micro Devices, Inc.
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+ ###############################################################################
26
+ import re
27
+ from typing import Optional
28
+
29
+ from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus
30
+ from nodescraper.interfaces import DataAnalyzer
31
+ from nodescraper.models import TaskResult
32
+
33
+ from .analyzer_args import KernelModuleAnalyzerArgs
34
+ from .kernel_module_data import KernelModuleDataModel
35
+
36
+
37
+ class KernelModuleAnalyzer(DataAnalyzer[KernelModuleDataModel, KernelModuleAnalyzerArgs]):
38
+ """Check kernel matches expected versions"""
39
+
40
+ DATA_MODEL = KernelModuleDataModel
41
+
42
+ def filter_modules_by_pattern(
43
+ self, modules: dict[str, dict], patterns: list[str] = None
44
+ ) -> tuple[dict[str, dict], list[str]]:
45
+ """Filter modules by pattern
46
+
47
+ Args:
48
+ modules (dict[str, dict]): modules to be filtered
49
+ patterns (list[str], optional): pattern to us. Defaults to None.
50
+
51
+ Returns:
52
+ tuple[dict[str, dict], list[str]]: tuple - dict of modules filtered,
53
+ list of unmatched pattern
54
+ """
55
+ if patterns is None:
56
+ return modules, []
57
+
58
+ matched_modules = {}
59
+ unmatched_patterns = []
60
+
61
+ pattern_match_flags = {p: False for p in patterns}
62
+
63
+ for mod_name in modules:
64
+ for p in patterns:
65
+ if re.search(p, mod_name, re.IGNORECASE):
66
+ matched_modules[mod_name] = modules[mod_name]
67
+ pattern_match_flags[p] = True
68
+ break
69
+
70
+ unmatched_patterns = [p for p, matched in pattern_match_flags.items() if not matched]
71
+
72
+ return matched_modules, unmatched_patterns
73
+
74
+ def filter_modules_by_name_and_param(
75
+ self, modules: dict[str, dict], to_match: dict[str, dict]
76
+ ) -> tuple[dict[str, dict], dict[str, dict]]:
77
+ """Filter modules by name, param and value
78
+
79
+ Args:
80
+ modules (dict[str, dict]): modules to be filtered
81
+ to_match (dict[str, dict]): modules to match
82
+
83
+ Returns:
84
+ tuple[dict[str, dict], dict[str, dict]]: tuple - dict of modules filtered,
85
+ dict of modules unmatched
86
+ """
87
+ if not to_match:
88
+ return modules, {}
89
+
90
+ filtered = {}
91
+ unmatched = {}
92
+
93
+ for mod_name, expected_data in to_match.items():
94
+ expected_params = expected_data.get("parameters", {})
95
+ actual_data = modules.get(mod_name)
96
+
97
+ if not actual_data:
98
+ unmatched[mod_name] = expected_data
99
+ continue
100
+
101
+ actual_params = actual_data.get("parameters", {})
102
+ param_mismatches = {}
103
+
104
+ for param, expected_val in expected_params.items():
105
+ actual_val = actual_params.get(param)
106
+ if actual_val != expected_val:
107
+ param_mismatches[param] = {
108
+ "expected": expected_val,
109
+ "actual": actual_val if actual_val is not None else "<missing>",
110
+ }
111
+
112
+ if param_mismatches:
113
+ unmatched[mod_name] = {"parameters": param_mismatches}
114
+ else:
115
+ filtered[mod_name] = actual_data
116
+
117
+ return filtered, unmatched
118
+
119
+ def analyze_data(
120
+ self, data: KernelModuleDataModel, args: Optional[KernelModuleAnalyzerArgs] = None
121
+ ) -> TaskResult:
122
+ """Analyze the kernel modules and associated parameters.
123
+
124
+ Args:
125
+ data (KernelModuleDataModel): KernelModule data to analyze.
126
+ args (Optional[KernelModuleAnalyzerArgs], optional): KernelModule analyzer args.
127
+
128
+ Returns:
129
+ TaskResult: Result of the analysis containing status and message.
130
+ """
131
+ if not args:
132
+ args = KernelModuleAnalyzerArgs()
133
+ else:
134
+ if args.regex_filter and args.kernel_modules:
135
+ self.logger.warning(
136
+ "Both regex_filter and kernel_modules provided in analyzer args. kernel_modules will be ignored"
137
+ )
138
+
139
+ self.result.status = ExecutionStatus.OK
140
+
141
+ if args.regex_filter:
142
+ try:
143
+ filtered_modules, unmatched_pattern = self.filter_modules_by_pattern(
144
+ data.kernel_modules, args.regex_filter
145
+ )
146
+ except re.error:
147
+ self._log_event(
148
+ category=EventCategory.RUNTIME,
149
+ description="KernelModule regex is invalid",
150
+ data={"regex_filters": {args.regex_filter}},
151
+ priority=EventPriority.ERROR,
152
+ )
153
+ self.result.message = (
154
+ f"Kernel modules failed to match regex. Regex: {args.regex_filter}"
155
+ )
156
+ self.result.status = ExecutionStatus.ERROR
157
+ return self.result
158
+
159
+ if unmatched_pattern:
160
+ self._log_event(
161
+ category=EventCategory.RUNTIME,
162
+ description="KernelModules did not match all patterns",
163
+ data={"unmatched_pattern: ": unmatched_pattern},
164
+ priority=EventPriority.INFO,
165
+ )
166
+ self.result.message = f"Kernel modules failed to match every pattern. Unmatched patterns: {unmatched_pattern}"
167
+ self.result.status = ExecutionStatus.ERROR
168
+ return self.result
169
+
170
+ self._log_event(
171
+ category=EventCategory.OS,
172
+ description="KernelModules analyzed",
173
+ data={"filtered_modules": filtered_modules},
174
+ priority=EventPriority.INFO,
175
+ )
176
+ return self.result
177
+
178
+ elif args.kernel_modules:
179
+ filtered_modules, not_matched = self.filter_modules_by_name_and_param(
180
+ data.kernel_modules, args.kernel_modules
181
+ )
182
+
183
+ # no modules matched
184
+ if not filtered_modules and not_matched:
185
+ self._log_event(
186
+ category=EventCategory.RUNTIME,
187
+ description="KernelModules: no modules matched",
188
+ data=args.kernel_modules,
189
+ priority=EventPriority.ERROR,
190
+ )
191
+ self.result.message = f"Kernel modules not matched: {not_matched}"
192
+ self.result.status = ExecutionStatus.ERROR
193
+ return self.result
194
+ # some modules matched
195
+ elif filtered_modules and not_matched:
196
+
197
+ self._log_event(
198
+ category=EventCategory.RUNTIME,
199
+ description="KernelModules: not all modules matched",
200
+ data=not_matched,
201
+ priority=EventPriority.ERROR,
202
+ )
203
+ self.result.message = f"Kernel modules not matched: {not_matched}"
204
+ self.result.status = ExecutionStatus.ERROR
205
+ return self.result
206
+ else:
207
+ self.result.message = (
208
+ "No values provided in analysis args for: kernel_modules and regex_match"
209
+ )
210
+ self.result.status = ExecutionStatus.NOT_RAN
211
+ return self.result