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,158 @@
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 datetime
27
+ import logging
28
+ import re
29
+ import uuid
30
+ from enum import Enum
31
+ from typing import Optional, Union
32
+
33
+ from pydantic import BaseModel, Field, field_serializer, field_validator
34
+
35
+ from nodescraper.enums import EventPriority
36
+
37
+ LOG_LEVEL_MAP = {
38
+ logging.INFO: EventPriority.INFO,
39
+ logging.WARNING: EventPriority.WARNING,
40
+ logging.ERROR: EventPriority.ERROR,
41
+ logging.CRITICAL: EventPriority.CRITICAL,
42
+ logging.FATAL: EventPriority.CRITICAL,
43
+ }
44
+
45
+
46
+ class Event(BaseModel):
47
+ """Base event class"""
48
+
49
+ id: uuid.UUID = Field(default_factory=uuid.uuid4)
50
+ timestamp: datetime.datetime = Field(
51
+ default_factory=lambda: datetime.datetime.now(datetime.timezone.utc)
52
+ )
53
+ reporter: str = "NODE_SCRAPER"
54
+ category: str
55
+ description: str
56
+ data: dict = Field(default_factory=dict)
57
+ priority: EventPriority
58
+ system_id: Optional[str] = None
59
+
60
+ @field_validator("timestamp")
61
+ @classmethod
62
+ def validate_timestamp(cls, timestamp: datetime.datetime) -> datetime.datetime:
63
+ """validate timestamp, will convert to utc timezone as long as input is timezone aware
64
+ Args:
65
+ timestamp (datetime): datetime object
66
+ Raises:
67
+ ValueError: if value is not a datetime object
68
+ ValueError: if value is not timezone aware
69
+ Returns:
70
+ datetime: validated datetime
71
+ """
72
+
73
+ if timestamp.tzinfo is None or timestamp.tzinfo.utcoffset(timestamp) is None:
74
+ raise ValueError("datetime must be timezone aware")
75
+
76
+ utc_offset = timestamp.utcoffset()
77
+ if utc_offset is not None and utc_offset.total_seconds() != 0:
78
+ timestamp = timestamp.astimezone(datetime.timezone.utc)
79
+
80
+ return timestamp
81
+
82
+ @field_validator("category", mode="before")
83
+ @classmethod
84
+ def validate_category(cls, category: Optional[Union[str, Enum]]) -> str:
85
+ """ensure category is has consistent formatting
86
+ Args:
87
+ category (str | Enum): category string
88
+ Returns:
89
+ str: formatted category string
90
+ """
91
+ if isinstance(category, Enum):
92
+ category = category.value
93
+
94
+ category = str(category).strip().upper()
95
+ category = re.sub(r"[\s-]", "_", category)
96
+ return category
97
+
98
+ @field_validator("priority", mode="before")
99
+ @classmethod
100
+ def validate_priority(cls, priority: Union[str, EventPriority]) -> EventPriority:
101
+ """Allow priority to be set via string priority name
102
+ Args:
103
+ priority (Union[str, EventPriority]): event priority string or enum
104
+ Raises:
105
+ ValueError: if priority string is an invalid value
106
+ Returns:
107
+ EventPriority: priority enum
108
+ """
109
+ if isinstance(priority, str):
110
+ try:
111
+ return getattr(EventPriority, priority.upper())
112
+ except AttributeError as e:
113
+ raise ValueError(
114
+ f"priority must be one of {[p.name for p in EventPriority]}"
115
+ ) from e
116
+ if isinstance(priority, EventPriority):
117
+ return priority
118
+ raise ValueError("priority must be an EventPriority or its name as a string")
119
+
120
+ @field_serializer("priority")
121
+ def serialize_priority(self, priority: EventPriority, _info) -> str:
122
+ """Use priority name when serializing events
123
+ Args:
124
+ priority (EventPriority): priority enum
125
+ Returns:
126
+ str: priority name string
127
+ """
128
+ return priority.name
129
+
130
+ @field_validator("data")
131
+ @classmethod
132
+ def validate_data(cls, data: dict) -> dict:
133
+ """Ensure data is below 100KB
134
+ Args:
135
+ data (dict): data input
136
+ Raises:
137
+ ValueError: When data is above 100KB in size
138
+ Returns:
139
+ dict: data output
140
+ """
141
+ if len(str(data).encode("utf-8")) >= (1024 * 100):
142
+ raise ValueError("Data must be below 100KB in size")
143
+ return data
144
+
145
+ @field_validator("description")
146
+ @classmethod
147
+ def validate_description(cls, desc: str) -> str:
148
+ """Ensure description is below 2KB
149
+ Args:
150
+ desc (str): description input
151
+ Raises:
152
+ ValueError: When desc is above 2KB in size
153
+ Returns:
154
+ str: desc output
155
+ """
156
+ if len(desc.encode("utf-8")) >= 1024 * 2:
157
+ raise ValueError("Description must be below 2KB in size")
158
+ return desc
@@ -0,0 +1,38 @@
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 pydantic import BaseModel, Field
29
+
30
+
31
+ class PluginConfig(BaseModel):
32
+ """Model for preset configuration of plugins and result collators"""
33
+
34
+ global_args: dict = Field(default_factory=dict)
35
+ plugins: dict[str, dict] = Field(default_factory=dict)
36
+ result_collators: dict[str, dict] = Field(default_factory=dict)
37
+ name: Optional[str] = None
38
+ desc: Optional[str] = None
@@ -0,0 +1,39 @@
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, Union
27
+
28
+ from pydantic import BaseModel
29
+
30
+ from nodescraper.enums import ExecutionStatus
31
+
32
+
33
+ class PluginResult(BaseModel):
34
+ """Object for result of a plugin"""
35
+
36
+ status: ExecutionStatus
37
+ source: str
38
+ message: Optional[str] = None
39
+ result_data: Optional[Union[dict, BaseModel]] = None
@@ -0,0 +1,44 @@
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
+ import platform
28
+ from typing import Optional
29
+
30
+ from pydantic import BaseModel, Field
31
+
32
+ from nodescraper.enums import OSFamily, SystemLocation
33
+
34
+
35
+ class SystemInfo(BaseModel):
36
+ """System object used to store data about System"""
37
+
38
+ name: str = platform.node()
39
+ os_family: OSFamily = OSFamily.UNKNOWN
40
+ sku: Optional[str] = None
41
+ platform: Optional[str] = None
42
+ metadata: Optional[dict] = Field(default_factory=dict)
43
+ location: Optional[SystemLocation] = SystemLocation.LOCAL
44
+ vendorid_ep: int = 0x1002
@@ -0,0 +1,185 @@
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 datetime
27
+ import logging
28
+ from typing import Any, Optional
29
+
30
+ from pydantic import BaseModel, Field, field_serializer, field_validator
31
+
32
+ from nodescraper.enums import EventPriority, ExecutionStatus
33
+
34
+ from .event import Event
35
+
36
+ STATUS_LOG_LEVEL_MAP = {
37
+ ExecutionStatus.UNSET: logging.INFO,
38
+ ExecutionStatus.NOT_RAN: logging.INFO,
39
+ ExecutionStatus.OK: logging.INFO,
40
+ ExecutionStatus.WARNING: logging.WARNING,
41
+ ExecutionStatus.ERROR: logging.ERROR,
42
+ ExecutionStatus.EXECUTION_FAILURE: logging.CRITICAL,
43
+ }
44
+
45
+
46
+ class TaskResult(BaseModel):
47
+ """Object for result of a task"""
48
+
49
+ status: ExecutionStatus = ExecutionStatus.UNSET
50
+ message: str = ""
51
+ task: Optional[str] = None
52
+ parent: Optional[str] = None
53
+ artifacts: list[BaseModel] = Field(default_factory=list)
54
+ events: list[Event] = Field(default_factory=list)
55
+ start_time: datetime.datetime = Field(default_factory=datetime.datetime.now)
56
+ end_time: datetime.datetime = Field(default_factory=datetime.datetime.now)
57
+
58
+ @field_serializer("status")
59
+ def serialize_status(self, status: ExecutionStatus, _info) -> str:
60
+ """Use status name when serializing result
61
+ Args:
62
+ status (ExecutionStatus): status enum
63
+ Returns:
64
+ str: status name string
65
+ """
66
+ return status.name
67
+
68
+ @field_validator("status", mode="before")
69
+ @classmethod
70
+ def validate_status(cls, v: Any):
71
+ """Validator to ensure `status` is a valid ExecutionStatus enum.
72
+
73
+ Args:
74
+ v (Any): The input value to validate (can be str or ExecutionStatus).
75
+
76
+ Returns:
77
+ ExecutionStatus: The validated enum value.
78
+
79
+ Raises:
80
+ ValueError: If the string is not a valid enum name.
81
+ """
82
+ if isinstance(v, ExecutionStatus):
83
+ return v
84
+ if isinstance(v, str):
85
+ try:
86
+ return ExecutionStatus[v]
87
+ except KeyError as err:
88
+ raise ValueError(f"Unknown status name: {v!r}") from err
89
+ return v
90
+
91
+ @property
92
+ def duration(self) -> Optional[str]:
93
+ """return duration of time as a string
94
+
95
+ Returns:
96
+ str: duration string
97
+ """
98
+ if self.start_time and self.end_time:
99
+ duration = str((self.end_time - self.start_time))
100
+ else:
101
+ duration = None
102
+
103
+ return duration
104
+
105
+ def _get_event_summary(self) -> str:
106
+ """Get summary string for events
107
+
108
+ Returns:
109
+ str: event summary with counts and descriptions
110
+ """
111
+ error_msg_counts: dict[str, int] = {}
112
+ warning_msg_counts: dict[str, int] = {}
113
+
114
+ for event in self.events:
115
+ if event.priority == EventPriority.WARNING:
116
+ warning_msg_counts[event.description] = (
117
+ warning_msg_counts.get(event.description, 0) + 1
118
+ )
119
+ elif event.priority >= EventPriority.ERROR:
120
+ error_msg_counts[event.description] = error_msg_counts.get(event.description, 0) + 1
121
+
122
+ summary_parts = []
123
+
124
+ if warning_msg_counts:
125
+ total_warnings = sum(warning_msg_counts.values())
126
+ warning_details = [
127
+ f"{msg} (x{count})" if count > 1 else msg
128
+ for msg, count in warning_msg_counts.items()
129
+ ]
130
+ summary_parts.append(f"{total_warnings} warnings: {', '.join(warning_details)}")
131
+
132
+ if error_msg_counts:
133
+ total_errors = sum(error_msg_counts.values())
134
+ error_details = [
135
+ f"{msg} (x{count})" if count > 1 else msg for msg, count in error_msg_counts.items()
136
+ ]
137
+ summary_parts.append(f"{total_errors} errors: {', '.join(error_details)}")
138
+
139
+ return "; ".join(summary_parts)
140
+
141
+ def _update_status(self) -> None:
142
+ """Update overall status based on event priority"""
143
+ self.status = ExecutionStatus.OK
144
+ for event in self.events:
145
+ if event.priority >= EventPriority.ERROR:
146
+ self.status = ExecutionStatus.ERROR
147
+ break
148
+ elif event.priority == EventPriority.WARNING:
149
+ self.status = ExecutionStatus.WARNING
150
+
151
+ def finalize(self, logger: Optional[logging.Logger] = None) -> None:
152
+ """Finalize the task result by setting end time, updating status, and logging
153
+ the result.
154
+
155
+ Args:
156
+ logger (Optional[logging.Logger], optional): python logger instance. Defaults to None.
157
+ """
158
+ self.end_time = datetime.datetime.now()
159
+
160
+ if self.status == ExecutionStatus.UNSET:
161
+ self._update_status()
162
+
163
+ if not self.message:
164
+ if self.status == ExecutionStatus.OK:
165
+ self.message = "task completed successfully"
166
+ elif self.status == ExecutionStatus.WARNING:
167
+ self.message = "task completed with warnings"
168
+ elif self.status == ExecutionStatus.NOT_RAN:
169
+ self.message = "task skipped"
170
+ elif self.status == ExecutionStatus.EXECUTION_FAILURE:
171
+ self.message = "task failed to run"
172
+ elif self.status == ExecutionStatus.ERROR:
173
+ self.message = "task detected errors"
174
+
175
+ event_summary = self._get_event_summary()
176
+ if event_summary:
177
+ self.message += f" ({event_summary})"
178
+
179
+ if logger:
180
+ logger.log(
181
+ STATUS_LOG_LEVEL_MAP.get(self.status, logging.INFO),
182
+ "(%s) %s",
183
+ self.parent,
184
+ self.message,
185
+ )
@@ -0,0 +1,38 @@
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 datetime
27
+ from typing import Optional
28
+
29
+ from nodescraper.models import AnalyzerArgs
30
+
31
+
32
+ class TimeRangeAnalysisArgs(AnalyzerArgs):
33
+ """
34
+ Model for time range analysis arguments.
35
+ """
36
+
37
+ analysis_range_start: Optional[datetime.datetime] = None
38
+ analysis_range_end: Optional[datetime.datetime] = None