amd-node-scraper 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- amd_node_scraper-0.0.1.dist-info/LICENSE +21 -0
- amd_node_scraper-0.0.1.dist-info/METADATA +424 -0
- amd_node_scraper-0.0.1.dist-info/RECORD +197 -0
- amd_node_scraper-0.0.1.dist-info/WHEEL +5 -0
- amd_node_scraper-0.0.1.dist-info/entry_points.txt +2 -0
- amd_node_scraper-0.0.1.dist-info/top_level.txt +1 -0
- nodescraper/__init__.py +32 -0
- nodescraper/base/__init__.py +34 -0
- nodescraper/base/inbandcollectortask.py +118 -0
- nodescraper/base/inbanddataplugin.py +39 -0
- nodescraper/base/regexanalyzer.py +120 -0
- nodescraper/cli/__init__.py +29 -0
- nodescraper/cli/cli.py +511 -0
- nodescraper/cli/constants.py +27 -0
- nodescraper/cli/dynamicparserbuilder.py +171 -0
- nodescraper/cli/helper.py +517 -0
- nodescraper/cli/inputargtypes.py +129 -0
- nodescraper/configbuilder.py +123 -0
- nodescraper/configregistry.py +66 -0
- nodescraper/configs/node_status.json +19 -0
- nodescraper/connection/__init__.py +25 -0
- nodescraper/connection/inband/__init__.py +46 -0
- nodescraper/connection/inband/inband.py +171 -0
- nodescraper/connection/inband/inbandlocal.py +93 -0
- nodescraper/connection/inband/inbandmanager.py +151 -0
- nodescraper/connection/inband/inbandremote.py +173 -0
- nodescraper/connection/inband/sshparams.py +43 -0
- nodescraper/constants.py +26 -0
- nodescraper/enums/__init__.py +40 -0
- nodescraper/enums/eventcategory.py +89 -0
- nodescraper/enums/eventpriority.py +42 -0
- nodescraper/enums/executionstatus.py +44 -0
- nodescraper/enums/osfamily.py +34 -0
- nodescraper/enums/systeminteraction.py +41 -0
- nodescraper/enums/systemlocation.py +33 -0
- nodescraper/generictypes.py +36 -0
- nodescraper/interfaces/__init__.py +44 -0
- nodescraper/interfaces/connectionmanager.py +143 -0
- nodescraper/interfaces/dataanalyzertask.py +138 -0
- nodescraper/interfaces/datacollectortask.py +185 -0
- nodescraper/interfaces/dataplugin.py +356 -0
- nodescraper/interfaces/plugin.py +127 -0
- nodescraper/interfaces/resultcollator.py +56 -0
- nodescraper/interfaces/task.py +164 -0
- nodescraper/interfaces/taskresulthook.py +39 -0
- nodescraper/models/__init__.py +48 -0
- nodescraper/models/analyzerargs.py +93 -0
- nodescraper/models/collectorargs.py +30 -0
- nodescraper/models/connectionconfig.py +34 -0
- nodescraper/models/datamodel.py +171 -0
- nodescraper/models/datapluginresult.py +39 -0
- nodescraper/models/event.py +158 -0
- nodescraper/models/pluginconfig.py +38 -0
- nodescraper/models/pluginresult.py +39 -0
- nodescraper/models/systeminfo.py +44 -0
- nodescraper/models/taskresult.py +185 -0
- nodescraper/models/timerangeargs.py +38 -0
- nodescraper/pluginexecutor.py +274 -0
- nodescraper/pluginregistry.py +152 -0
- nodescraper/plugins/__init__.py +25 -0
- nodescraper/plugins/inband/__init__.py +25 -0
- nodescraper/plugins/inband/amdsmi/__init__.py +28 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_analyzer.py +821 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_collector.py +1313 -0
- nodescraper/plugins/inband/amdsmi/amdsmi_plugin.py +43 -0
- nodescraper/plugins/inband/amdsmi/amdsmidata.py +1002 -0
- nodescraper/plugins/inband/amdsmi/analyzer_args.py +50 -0
- nodescraper/plugins/inband/amdsmi/cper.py +65 -0
- nodescraper/plugins/inband/bios/__init__.py +29 -0
- nodescraper/plugins/inband/bios/analyzer_args.py +64 -0
- nodescraper/plugins/inband/bios/bios_analyzer.py +93 -0
- nodescraper/plugins/inband/bios/bios_collector.py +93 -0
- nodescraper/plugins/inband/bios/bios_plugin.py +43 -0
- nodescraper/plugins/inband/bios/biosdata.py +30 -0
- nodescraper/plugins/inband/cmdline/__init__.py +25 -0
- nodescraper/plugins/inband/cmdline/analyzer_args.py +80 -0
- nodescraper/plugins/inband/cmdline/cmdline_analyzer.py +113 -0
- nodescraper/plugins/inband/cmdline/cmdline_collector.py +77 -0
- nodescraper/plugins/inband/cmdline/cmdline_plugin.py +43 -0
- nodescraper/plugins/inband/cmdline/cmdlinedata.py +30 -0
- nodescraper/plugins/inband/device_enumeration/__init__.py +29 -0
- nodescraper/plugins/inband/device_enumeration/analyzer_args.py +73 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_analyzer.py +81 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_collector.py +176 -0
- nodescraper/plugins/inband/device_enumeration/device_enumeration_plugin.py +45 -0
- nodescraper/plugins/inband/device_enumeration/deviceenumdata.py +36 -0
- nodescraper/plugins/inband/dimm/__init__.py +25 -0
- nodescraper/plugins/inband/dimm/collector_args.py +31 -0
- nodescraper/plugins/inband/dimm/dimm_collector.py +151 -0
- nodescraper/plugins/inband/dimm/dimm_plugin.py +40 -0
- nodescraper/plugins/inband/dimm/dimmdata.py +30 -0
- nodescraper/plugins/inband/dkms/__init__.py +25 -0
- nodescraper/plugins/inband/dkms/analyzer_args.py +85 -0
- nodescraper/plugins/inband/dkms/dkms_analyzer.py +106 -0
- nodescraper/plugins/inband/dkms/dkms_collector.py +76 -0
- nodescraper/plugins/inband/dkms/dkms_plugin.py +43 -0
- nodescraper/plugins/inband/dkms/dkmsdata.py +33 -0
- nodescraper/plugins/inband/dmesg/__init__.py +28 -0
- nodescraper/plugins/inband/dmesg/analyzer_args.py +33 -0
- nodescraper/plugins/inband/dmesg/collector_args.py +39 -0
- nodescraper/plugins/inband/dmesg/dmesg_analyzer.py +503 -0
- nodescraper/plugins/inband/dmesg/dmesg_collector.py +164 -0
- nodescraper/plugins/inband/dmesg/dmesg_plugin.py +44 -0
- nodescraper/plugins/inband/dmesg/dmesgdata.py +116 -0
- nodescraper/plugins/inband/fabrics/__init__.py +28 -0
- nodescraper/plugins/inband/fabrics/fabrics_collector.py +726 -0
- nodescraper/plugins/inband/fabrics/fabrics_plugin.py +37 -0
- nodescraper/plugins/inband/fabrics/fabricsdata.py +140 -0
- nodescraper/plugins/inband/journal/__init__.py +28 -0
- nodescraper/plugins/inband/journal/collector_args.py +33 -0
- nodescraper/plugins/inband/journal/journal_collector.py +107 -0
- nodescraper/plugins/inband/journal/journal_plugin.py +40 -0
- nodescraper/plugins/inband/journal/journaldata.py +44 -0
- nodescraper/plugins/inband/kernel/__init__.py +25 -0
- nodescraper/plugins/inband/kernel/analyzer_args.py +64 -0
- nodescraper/plugins/inband/kernel/kernel_analyzer.py +91 -0
- nodescraper/plugins/inband/kernel/kernel_collector.py +129 -0
- nodescraper/plugins/inband/kernel/kernel_plugin.py +43 -0
- nodescraper/plugins/inband/kernel/kerneldata.py +32 -0
- nodescraper/plugins/inband/kernel_module/__init__.py +25 -0
- nodescraper/plugins/inband/kernel_module/analyzer_args.py +59 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_analyzer.py +211 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_collector.py +264 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_data.py +60 -0
- nodescraper/plugins/inband/kernel_module/kernel_module_plugin.py +43 -0
- nodescraper/plugins/inband/memory/__init__.py +25 -0
- nodescraper/plugins/inband/memory/analyzer_args.py +45 -0
- nodescraper/plugins/inband/memory/memory_analyzer.py +98 -0
- nodescraper/plugins/inband/memory/memory_collector.py +330 -0
- nodescraper/plugins/inband/memory/memory_plugin.py +43 -0
- nodescraper/plugins/inband/memory/memorydata.py +90 -0
- nodescraper/plugins/inband/network/__init__.py +28 -0
- nodescraper/plugins/inband/network/network_collector.py +1828 -0
- nodescraper/plugins/inband/network/network_plugin.py +37 -0
- nodescraper/plugins/inband/network/networkdata.py +319 -0
- nodescraper/plugins/inband/nvme/__init__.py +28 -0
- nodescraper/plugins/inband/nvme/nvme_collector.py +167 -0
- nodescraper/plugins/inband/nvme/nvme_plugin.py +37 -0
- nodescraper/plugins/inband/nvme/nvmedata.py +45 -0
- nodescraper/plugins/inband/os/__init__.py +25 -0
- nodescraper/plugins/inband/os/analyzer_args.py +64 -0
- nodescraper/plugins/inband/os/os_analyzer.py +73 -0
- nodescraper/plugins/inband/os/os_collector.py +131 -0
- nodescraper/plugins/inband/os/os_plugin.py +43 -0
- nodescraper/plugins/inband/os/osdata.py +31 -0
- nodescraper/plugins/inband/package/__init__.py +25 -0
- nodescraper/plugins/inband/package/analyzer_args.py +48 -0
- nodescraper/plugins/inband/package/package_analyzer.py +253 -0
- nodescraper/plugins/inband/package/package_collector.py +273 -0
- nodescraper/plugins/inband/package/package_plugin.py +43 -0
- nodescraper/plugins/inband/package/packagedata.py +41 -0
- nodescraper/plugins/inband/pcie/__init__.py +29 -0
- nodescraper/plugins/inband/pcie/analyzer_args.py +63 -0
- nodescraper/plugins/inband/pcie/pcie_analyzer.py +1081 -0
- nodescraper/plugins/inband/pcie/pcie_collector.py +690 -0
- nodescraper/plugins/inband/pcie/pcie_data.py +2017 -0
- nodescraper/plugins/inband/pcie/pcie_plugin.py +43 -0
- nodescraper/plugins/inband/process/__init__.py +25 -0
- nodescraper/plugins/inband/process/analyzer_args.py +45 -0
- nodescraper/plugins/inband/process/collector_args.py +31 -0
- nodescraper/plugins/inband/process/process_analyzer.py +91 -0
- nodescraper/plugins/inband/process/process_collector.py +115 -0
- nodescraper/plugins/inband/process/process_plugin.py +46 -0
- nodescraper/plugins/inband/process/processdata.py +34 -0
- nodescraper/plugins/inband/rocm/__init__.py +25 -0
- nodescraper/plugins/inband/rocm/analyzer_args.py +66 -0
- nodescraper/plugins/inband/rocm/rocm_analyzer.py +100 -0
- nodescraper/plugins/inband/rocm/rocm_collector.py +205 -0
- nodescraper/plugins/inband/rocm/rocm_plugin.py +43 -0
- nodescraper/plugins/inband/rocm/rocmdata.py +62 -0
- nodescraper/plugins/inband/storage/__init__.py +25 -0
- nodescraper/plugins/inband/storage/analyzer_args.py +38 -0
- nodescraper/plugins/inband/storage/collector_args.py +31 -0
- nodescraper/plugins/inband/storage/storage_analyzer.py +152 -0
- nodescraper/plugins/inband/storage/storage_collector.py +110 -0
- nodescraper/plugins/inband/storage/storage_plugin.py +44 -0
- nodescraper/plugins/inband/storage/storagedata.py +70 -0
- nodescraper/plugins/inband/sysctl/__init__.py +29 -0
- nodescraper/plugins/inband/sysctl/analyzer_args.py +67 -0
- nodescraper/plugins/inband/sysctl/sysctl_analyzer.py +81 -0
- nodescraper/plugins/inband/sysctl/sysctl_collector.py +101 -0
- nodescraper/plugins/inband/sysctl/sysctl_plugin.py +43 -0
- nodescraper/plugins/inband/sysctl/sysctldata.py +42 -0
- nodescraper/plugins/inband/syslog/__init__.py +28 -0
- nodescraper/plugins/inband/syslog/syslog_collector.py +121 -0
- nodescraper/plugins/inband/syslog/syslog_plugin.py +37 -0
- nodescraper/plugins/inband/syslog/syslogdata.py +46 -0
- nodescraper/plugins/inband/uptime/__init__.py +25 -0
- nodescraper/plugins/inband/uptime/uptime_collector.py +88 -0
- nodescraper/plugins/inband/uptime/uptime_plugin.py +37 -0
- nodescraper/plugins/inband/uptime/uptimedata.py +31 -0
- nodescraper/resultcollators/__init__.py +25 -0
- nodescraper/resultcollators/tablesummary.py +159 -0
- nodescraper/taskresulthooks/__init__.py +28 -0
- nodescraper/taskresulthooks/filesystemloghook.py +88 -0
- nodescraper/typeutils.py +171 -0
- nodescraper/utils.py +412 -0
|
@@ -0,0 +1,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
|