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,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 argparse
|
|
27
|
+
import json
|
|
28
|
+
from typing import Generic, Optional, Type
|
|
29
|
+
|
|
30
|
+
from pydantic import ValidationError
|
|
31
|
+
|
|
32
|
+
from nodescraper.generictypes import TModelType
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def log_path_arg(log_path: str) -> Optional[str]:
|
|
36
|
+
"""Type function for a log path arg, allows 'none' to be specified to disable logging
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
log_path (str): log path string
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Optional[str]: log path or None
|
|
43
|
+
"""
|
|
44
|
+
if log_path.lower() == "none":
|
|
45
|
+
return None
|
|
46
|
+
return log_path
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def bool_arg(str_input: str) -> bool:
|
|
50
|
+
"""Converts a string arg input into a bool
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
str_input (str): string input
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
bool: bool value for string
|
|
57
|
+
"""
|
|
58
|
+
if str_input.lower() == "true":
|
|
59
|
+
return True
|
|
60
|
+
elif str_input.lower() == "false":
|
|
61
|
+
return False
|
|
62
|
+
raise argparse.ArgumentTypeError("Invalid input, boolean value (True or False) expected")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def dict_arg(str_input: str) -> dict:
|
|
66
|
+
"""converts a json string into a dict
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
str_input (str): input string
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
argparse.ArgumentTypeError: if error was seen when loading string into json dict
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
dict: dict representation of the json string
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
return json.loads(str_input)
|
|
79
|
+
except json.JSONDecodeError as e:
|
|
80
|
+
raise argparse.ArgumentTypeError("Invalid json input for arg") from e
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ModelArgHandler(Generic[TModelType]):
|
|
84
|
+
"""Class to handle loading json files into pydantic models"""
|
|
85
|
+
|
|
86
|
+
def __init__(self, model: Type[TModelType]) -> None:
|
|
87
|
+
self.model = model
|
|
88
|
+
|
|
89
|
+
def process_file_arg(self, file_path: str) -> TModelType:
|
|
90
|
+
"""load a json file into a pydantic model
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
file_path (str): json file path
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
argparse.ArgumentTypeError: If validation errors were seen when building model
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
TModelType: model instance
|
|
100
|
+
"""
|
|
101
|
+
data = json_arg(file_path)
|
|
102
|
+
try:
|
|
103
|
+
return self.model(**data)
|
|
104
|
+
except ValidationError as e:
|
|
105
|
+
raise argparse.ArgumentTypeError(
|
|
106
|
+
f"Validation errors when processing {file_path}: {e.errors(include_url=False)}"
|
|
107
|
+
) from e
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def json_arg(json_path: str) -> dict:
|
|
111
|
+
"""loads a json file into a dict
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
json_path (str): path to json file
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
argparse.ArgumentTypeError: If file does not exist or could not be decoded
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
dict: output dict
|
|
121
|
+
"""
|
|
122
|
+
try:
|
|
123
|
+
with open(json_path, "r", encoding="utf-8") as input_file:
|
|
124
|
+
data = json.load(input_file)
|
|
125
|
+
return data
|
|
126
|
+
except json.JSONDecodeError as e:
|
|
127
|
+
raise argparse.ArgumentTypeError(f"File {json_path} contains invalid JSON") from e
|
|
128
|
+
except FileNotFoundError as e:
|
|
129
|
+
raise argparse.ArgumentTypeError(f"Unable to find file: {json_path}") from e
|
|
@@ -0,0 +1,123 @@
|
|
|
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 enum
|
|
27
|
+
import logging
|
|
28
|
+
from typing import Any, Optional, Type, Union
|
|
29
|
+
|
|
30
|
+
from pydantic import BaseModel
|
|
31
|
+
|
|
32
|
+
from nodescraper.interfaces import PluginInterface
|
|
33
|
+
from nodescraper.models import DataModel, PluginConfig
|
|
34
|
+
from nodescraper.pluginregistry import PluginRegistry
|
|
35
|
+
from nodescraper.typeutils import TypeData, TypeUtils
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ConfigBuilder:
|
|
39
|
+
"""Class used to dynamically generate plugin configs"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, plugin_registry: PluginRegistry, logger: Optional[logging.Logger] = None):
|
|
42
|
+
self.plugin_registry = plugin_registry
|
|
43
|
+
self.logger = logger if logger else logging.getLogger()
|
|
44
|
+
|
|
45
|
+
def gen_config(self, plugin_names: list[str]) -> PluginConfig:
|
|
46
|
+
"""Generate a plugin config dict for a list of plugin names
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
plugin_names (list[str]): list of plugin names to include in the config
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
dict: plugin config dict
|
|
53
|
+
"""
|
|
54
|
+
config = PluginConfig()
|
|
55
|
+
for plugin in plugin_names:
|
|
56
|
+
if plugin in self.plugin_registry.plugins:
|
|
57
|
+
config.plugins[plugin] = self._build_plugin_config(
|
|
58
|
+
self.plugin_registry.plugins[plugin]
|
|
59
|
+
)
|
|
60
|
+
else:
|
|
61
|
+
self.logger.warning("No plugin found with name: %s", plugin)
|
|
62
|
+
return config
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def _build_plugin_config(cls, plugin_class: Type[PluginInterface]) -> dict:
|
|
66
|
+
type_map = TypeUtils.get_func_arg_types(plugin_class.run, plugin_class)
|
|
67
|
+
config = {}
|
|
68
|
+
|
|
69
|
+
for arg, arg_data in type_map.items():
|
|
70
|
+
cls._update_config(arg, arg_data, config)
|
|
71
|
+
|
|
72
|
+
return config
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def _update_config(cls, config_key, type_data: TypeData, config: dict):
|
|
76
|
+
if config_key in ["self", "preserve_connection", "max_event_priority_level"]:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
type_class_map = {
|
|
80
|
+
type_class.type_class: type_class for type_class in type_data.type_classes
|
|
81
|
+
}
|
|
82
|
+
if type(None) in type_class_map:
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
model_arg = next(
|
|
86
|
+
(
|
|
87
|
+
type_class
|
|
88
|
+
for type_class in type_class_map
|
|
89
|
+
if (isinstance(type_class, type) and issubclass(type_class, BaseModel))
|
|
90
|
+
and not issubclass(type_class, DataModel)
|
|
91
|
+
),
|
|
92
|
+
None,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if model_arg:
|
|
96
|
+
model_config = {}
|
|
97
|
+
for attr, attr_data in TypeUtils.get_model_types(model_arg).items():
|
|
98
|
+
cls._update_config(attr, attr_data, model_config)
|
|
99
|
+
config[config_key] = model_config
|
|
100
|
+
else:
|
|
101
|
+
config[config_key] = cls._process_value(type_data.default)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def _process_value(cls, value: Any) -> Optional[Union[dict, str, int, float, list]]:
|
|
105
|
+
if isinstance(value, enum.Enum):
|
|
106
|
+
return value.name
|
|
107
|
+
|
|
108
|
+
if isinstance(value, dict):
|
|
109
|
+
return_dict = {}
|
|
110
|
+
for key, val in value.items():
|
|
111
|
+
return_dict[key] = cls._process_value(val)
|
|
112
|
+
|
|
113
|
+
elif not isinstance(
|
|
114
|
+
value,
|
|
115
|
+
(
|
|
116
|
+
str,
|
|
117
|
+
int,
|
|
118
|
+
float,
|
|
119
|
+
),
|
|
120
|
+
):
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
return value
|
|
@@ -0,0 +1,66 @@
|
|
|
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 json
|
|
27
|
+
import os
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Optional
|
|
30
|
+
|
|
31
|
+
from pydantic import ValidationError
|
|
32
|
+
|
|
33
|
+
from nodescraper.models import PluginConfig
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ConfigRegistry:
|
|
37
|
+
"""Class to load json plugin configs into models"""
|
|
38
|
+
|
|
39
|
+
INTERNAL_SEARCH_PATH = os.path.join(os.path.dirname(__file__), "configs")
|
|
40
|
+
|
|
41
|
+
def __init__(self, config_path: Optional[str] = None) -> None:
|
|
42
|
+
self.configs = {}
|
|
43
|
+
self.load_configs(config_path)
|
|
44
|
+
|
|
45
|
+
def load_configs(self, config_path: Optional[str] = None):
|
|
46
|
+
"""load plugin config json files into pydantic models
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
config_path (Optional[str], optional): Path in which to search for config files. Defaults to None.
|
|
50
|
+
"""
|
|
51
|
+
if not config_path:
|
|
52
|
+
config_path = self.INTERNAL_SEARCH_PATH
|
|
53
|
+
|
|
54
|
+
config_path = Path(config_path)
|
|
55
|
+
|
|
56
|
+
for config_file in config_path.glob("*.json"):
|
|
57
|
+
with open(config_file, "r", encoding="utf-8") as in_file:
|
|
58
|
+
try:
|
|
59
|
+
file_data = json.load(in_file)
|
|
60
|
+
config_model = PluginConfig(**file_data)
|
|
61
|
+
if config_model.name:
|
|
62
|
+
self.configs[config_model.name] = config_model
|
|
63
|
+
else:
|
|
64
|
+
self.configs[config_file.name] = config_model
|
|
65
|
+
except (ValidationError, json.JSONDecodeError):
|
|
66
|
+
pass
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "NodeStatus",
|
|
3
|
+
"desc": "Check configuration and status of the node",
|
|
4
|
+
"global_args": {},
|
|
5
|
+
"plugins": {
|
|
6
|
+
"BiosPlugin": {},
|
|
7
|
+
"CmdlinePlugin": {},
|
|
8
|
+
"DimmPlugin": {},
|
|
9
|
+
"DkmsPlugin": {},
|
|
10
|
+
"DmesgPlugin": {},
|
|
11
|
+
"KernelPlugin": {},
|
|
12
|
+
"MemoryPlugin": {},
|
|
13
|
+
"OsPlugin": {},
|
|
14
|
+
"RocmPlugin": {},
|
|
15
|
+
"StoragePlugin": {},
|
|
16
|
+
"UptimePlugin": {}
|
|
17
|
+
},
|
|
18
|
+
"result_collators": {}
|
|
19
|
+
}
|
|
@@ -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,46 @@
|
|
|
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 .inband import (
|
|
27
|
+
BaseFileArtifact,
|
|
28
|
+
BinaryFileArtifact,
|
|
29
|
+
CommandArtifact,
|
|
30
|
+
InBandConnection,
|
|
31
|
+
TextFileArtifact,
|
|
32
|
+
)
|
|
33
|
+
from .inbandlocal import LocalShell
|
|
34
|
+
from .inbandmanager import InBandConnectionManager
|
|
35
|
+
from .sshparams import SSHConnectionParams
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"SSHConnectionParams",
|
|
39
|
+
"LocalShell",
|
|
40
|
+
"InBandConnectionManager",
|
|
41
|
+
"InBandConnection",
|
|
42
|
+
"BaseFileArtifact",
|
|
43
|
+
"TextFileArtifact",
|
|
44
|
+
"BinaryFileArtifact",
|
|
45
|
+
"CommandArtifact",
|
|
46
|
+
]
|
|
@@ -0,0 +1,171 @@
|
|
|
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 abc
|
|
27
|
+
import os
|
|
28
|
+
from typing import Optional
|
|
29
|
+
|
|
30
|
+
from pydantic import BaseModel
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CommandArtifact(BaseModel):
|
|
34
|
+
"""Artifact for the result of shell command execution"""
|
|
35
|
+
|
|
36
|
+
command: str
|
|
37
|
+
stdout: str
|
|
38
|
+
stderr: str
|
|
39
|
+
exit_code: int
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BaseFileArtifact(BaseModel, abc.ABC):
|
|
43
|
+
"""Base class for files"""
|
|
44
|
+
|
|
45
|
+
filename: str
|
|
46
|
+
|
|
47
|
+
@abc.abstractmethod
|
|
48
|
+
def log_model(self, log_path: str) -> None:
|
|
49
|
+
"""Write file to path
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
log_path (str): Path for file
|
|
53
|
+
"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abc.abstractmethod
|
|
57
|
+
def contents_str(self) -> str:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_bytes(
|
|
62
|
+
cls,
|
|
63
|
+
filename: str,
|
|
64
|
+
raw_contents: bytes,
|
|
65
|
+
encoding: Optional[str] = "utf-8",
|
|
66
|
+
strip: bool = True,
|
|
67
|
+
) -> "BaseFileArtifact":
|
|
68
|
+
"""factory method
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
filename (str): name of file to be read
|
|
72
|
+
raw_contents (bytes): Raw file content
|
|
73
|
+
encoding (Optional[str], optional): Optional encoding. Defaults to "utf-8".
|
|
74
|
+
strip (bool, optional): Remove padding. Defaults to True.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
BaseFileArtifact: _Returns instance of Artifact file
|
|
78
|
+
"""
|
|
79
|
+
if encoding is None:
|
|
80
|
+
return BinaryFileArtifact(filename=filename, contents=raw_contents)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
text = raw_contents.decode(encoding)
|
|
84
|
+
return TextFileArtifact(filename=filename, contents=text.strip() if strip else text)
|
|
85
|
+
except UnicodeDecodeError:
|
|
86
|
+
return BinaryFileArtifact(filename=filename, contents=raw_contents)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class TextFileArtifact(BaseFileArtifact):
|
|
90
|
+
"""Class for text file artifacts"""
|
|
91
|
+
|
|
92
|
+
contents: str
|
|
93
|
+
|
|
94
|
+
def log_model(self, log_path: str) -> None:
|
|
95
|
+
"""Write file to disk
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
log_path (str): Path for file
|
|
99
|
+
"""
|
|
100
|
+
path = os.path.join(log_path, self.filename)
|
|
101
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
102
|
+
f.write(self.contents)
|
|
103
|
+
|
|
104
|
+
def contents_str(self) -> str:
|
|
105
|
+
"""Get content as str
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
str: Str instance of file content
|
|
109
|
+
"""
|
|
110
|
+
return self.contents
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class BinaryFileArtifact(BaseFileArtifact):
|
|
114
|
+
"""Class for binary file artifacts"""
|
|
115
|
+
|
|
116
|
+
contents: bytes
|
|
117
|
+
|
|
118
|
+
def log_model(self, log_path: str) -> None:
|
|
119
|
+
"""Write file to disk
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
log_path (str): Path for file
|
|
123
|
+
"""
|
|
124
|
+
log_name = os.path.join(log_path, self.filename)
|
|
125
|
+
with open(log_name, "wb") as f:
|
|
126
|
+
f.write(self.contents)
|
|
127
|
+
|
|
128
|
+
def contents_str(self) -> str:
|
|
129
|
+
"""File content
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
str: Str instance of file content
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
return self.contents.decode("utf-8")
|
|
136
|
+
except UnicodeDecodeError:
|
|
137
|
+
return f"<binary data: {len(self.contents)} bytes>"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class InBandConnection(abc.ABC):
|
|
141
|
+
|
|
142
|
+
@abc.abstractmethod
|
|
143
|
+
def run_command(
|
|
144
|
+
self, command: str, sudo: bool = False, timeout: int = 300, strip: bool = True
|
|
145
|
+
) -> CommandArtifact:
|
|
146
|
+
"""Run an in band shell command
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
command (str): command to run
|
|
150
|
+
sudo (bool, optional): run command with sudo (Linux only). Defaults to False.
|
|
151
|
+
timeout (int, optional): timeout for command in seconds. Defaults to 300.
|
|
152
|
+
strip (bool, optional): strip output of command. Defaults to True.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
CommandArtifact: command result object
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
@abc.abstractmethod
|
|
159
|
+
def read_file(
|
|
160
|
+
self, filename: str, encoding: str = "utf-8", strip: bool = True
|
|
161
|
+
) -> BaseFileArtifact:
|
|
162
|
+
"""Read a file into a BaseFileArtifact
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
filename (str): filename
|
|
166
|
+
encoding (str, optional): encoding to use when opening file. Defaults to "utf-8".
|
|
167
|
+
strip (bool): automatically strip file contents
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
BaseFileArtifact: file artifact
|
|
171
|
+
"""
|
|
@@ -0,0 +1,93 @@
|
|
|
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 os
|
|
27
|
+
import subprocess
|
|
28
|
+
|
|
29
|
+
from .inband import (
|
|
30
|
+
BaseFileArtifact,
|
|
31
|
+
CommandArtifact,
|
|
32
|
+
InBandConnection,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LocalShell(InBandConnection):
|
|
37
|
+
|
|
38
|
+
def run_command(
|
|
39
|
+
self, command: str, sudo: bool = False, timeout: int = 300, strip: bool = True
|
|
40
|
+
) -> CommandArtifact:
|
|
41
|
+
"""Run a local in band shell command
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
command (str): command to run
|
|
45
|
+
sudo (bool, optional): run command with sudo (Linux only). Defaults to False.
|
|
46
|
+
timeout (int, optional): timeout for command in seconds. Defaults to 300.
|
|
47
|
+
strip (bool, optional): strip output of command. Defaults to True.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
CommandArtifact: command result object
|
|
51
|
+
"""
|
|
52
|
+
if sudo:
|
|
53
|
+
command = f"sudo {command}"
|
|
54
|
+
|
|
55
|
+
res = subprocess.run(
|
|
56
|
+
command,
|
|
57
|
+
encoding="utf-8",
|
|
58
|
+
shell=True,
|
|
59
|
+
errors="replace",
|
|
60
|
+
timeout=timeout,
|
|
61
|
+
capture_output=True,
|
|
62
|
+
check=False,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return CommandArtifact(
|
|
66
|
+
command=command,
|
|
67
|
+
stdout=res.stdout.strip() if strip else res.stdout,
|
|
68
|
+
stderr=res.stderr.strip() if strip else res.stderr,
|
|
69
|
+
exit_code=res.returncode,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def read_file(
|
|
73
|
+
self, filename: str, encoding: str = "utf-8", strip: bool = True
|
|
74
|
+
) -> BaseFileArtifact:
|
|
75
|
+
"""Read a local file into a BaseFileArtifact
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
filename (str): filename
|
|
79
|
+
encoding (str, optional): encoding to use when opening file. Defaults to "utf-8".
|
|
80
|
+
strip (bool): automatically strip file contents
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
BaseFileArtifact: file artifact
|
|
84
|
+
"""
|
|
85
|
+
with open(filename, "rb") as f:
|
|
86
|
+
raw_contents = f.read()
|
|
87
|
+
|
|
88
|
+
return BaseFileArtifact.from_bytes(
|
|
89
|
+
filename=os.path.basename(filename),
|
|
90
|
+
raw_contents=raw_contents,
|
|
91
|
+
encoding=encoding,
|
|
92
|
+
strip=strip,
|
|
93
|
+
)
|