Skip to content

Commit 04b2957

Browse files
committed
merged develop
2 parents 86a8711 + d44d952 commit 04b2957

21 files changed

+599
-29
lines changed

nodescraper/cli/cli.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,13 @@ def build_parser(
154154
help="Generate reference config from system. Writes to ./reference_config.json.",
155155
)
156156

157+
parser.add_argument(
158+
"--skip-sudo",
159+
dest="skip_sudo",
160+
action="store_true",
161+
help="Skip plugins that require sudo permissions",
162+
)
163+
157164
subparsers = parser.add_subparsers(dest="subcmd", help="Subcommands")
158165

159166
summary_parser = subparsers.add_parser(
@@ -418,6 +425,11 @@ def main(arg_input: Optional[list[str]] = None):
418425
plugin_subparser_map=plugin_subparser_map,
419426
)
420427

428+
if parsed_args.skip_sudo:
429+
plugin_config_inst_list[-1].global_args.setdefault("collection_args", {})[
430+
"skip_sudo"
431+
] = True
432+
421433
log_system_info(log_path, system_info, logger)
422434
except Exception as e:
423435
parser.error(str(e))
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
###############################################################################
2+
#
3+
# MIT License
4+
#
5+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
#
25+
###############################################################################
26+
27+
from nodescraper.models import CollectorArgs
28+
29+
30+
class DimmCollectorArgs(CollectorArgs):
31+
skip_sudo: bool = False

nodescraper/plugins/inband/dimm/dimm_collector.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,29 @@
2323
# SOFTWARE.
2424
#
2525
###############################################################################
26+
from typing import Optional
27+
2628
from nodescraper.base import InBandDataCollector
2729
from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus, OSFamily
2830
from nodescraper.models import TaskResult
2931

32+
from .collector_args import DimmCollectorArgs
3033
from .dimmdata import DimmDataModel
3134

3235

33-
class DimmCollector(InBandDataCollector[DimmDataModel, None]):
36+
class DimmCollector(InBandDataCollector[DimmDataModel, DimmCollectorArgs]):
3437
"""Collect data on installed DIMMs"""
3538

3639
DATA_MODEL = DimmDataModel
3740

3841
def collect_data(
3942
self,
40-
args=None,
43+
args: Optional[DimmCollectorArgs] = None,
4144
) -> tuple[TaskResult, DimmDataModel | None]:
4245
"""Collect data on installed DIMMs"""
46+
if args is None:
47+
args = DimmCollectorArgs()
48+
4349
dimm_str = None
4450
if self.system_info.os_family == OSFamily.WINDOWS:
4551
res = self._run_sut_cmd("wmic memorychip get Capacity")
@@ -59,6 +65,10 @@ def collect_data(
5965
for capacity, count in capacities.items():
6066
dimm_str += f"{count} x {capacity / 1024 / 1024:.2f}GB "
6167
else:
68+
if args.skip_sudo:
69+
self.result.message = "Skipping sudo plugin"
70+
self.result.status = ExecutionStatus.NOT_RAN
71+
return self.result, None
6272
res = self._run_sut_cmd(
6373
"""sh -c 'dmidecode -t 17 | tr -s " " | grep -v "Volatile\\|None\\|Module" | grep Size' 2>/dev/null""",
6474
sudo=True,

nodescraper/plugins/inband/dimm/dimm_plugin.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@
2525
###############################################################################
2626
from nodescraper.base import InBandDataPlugin
2727

28+
from .collector_args import DimmCollectorArgs
2829
from .dimm_collector import DimmCollector
2930
from .dimmdata import DimmDataModel
3031

3132

32-
class DimmPlugin(InBandDataPlugin[DimmDataModel, None, None]):
33+
class DimmPlugin(InBandDataPlugin[DimmDataModel, DimmCollectorArgs, None]):
3334
"""Plugin for collection and analysis of DIMM data"""
3435

3536
DATA_MODEL = DimmDataModel
3637

3738
COLLECTOR = DimmCollector
39+
40+
COLLECTOR_ARGS = DimmCollectorArgs

nodescraper/plugins/inband/dmesg/collector_args.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ class DmesgCollectorArgs(CollectorArgs):
3535
"""
3636

3737
collect_rotated_logs: bool = False
38+
skip_sudo: bool = False

nodescraper/plugins/inband/dmesg/dmesg_collector.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@
2828

2929
from nodescraper.base import InBandDataCollector
3030
from nodescraper.connection.inband import TextFileArtifact
31-
from nodescraper.enums import EventCategory, EventPriority, OSFamily
31+
from nodescraper.enums import EventCategory, EventPriority, ExecutionStatus, OSFamily
3232
from nodescraper.models import TaskResult
3333

3434
from .collector_args import DmesgCollectorArgs
3535
from .dmesgdata import DmesgData
3636

3737

38-
class DmesgCollector(InBandDataCollector[DmesgData, None]):
38+
class DmesgCollector(InBandDataCollector[DmesgData, DmesgCollectorArgs]):
3939
"""Read dmesg log"""
4040

4141
SUPPORTED_OS_FAMILY = {OSFamily.LINUX}
@@ -179,10 +179,14 @@ def collect_data(
179179
Returns:
180180
tuple[TaskResult, DmesgData | None]: tuple containing the result of the task and the dmesg data if available
181181
"""
182-
183182
if args is None:
184183
args = DmesgCollectorArgs()
185184

185+
if args.skip_sudo:
186+
self.result.message = "Skipping sudo plugin"
187+
self.result.status = ExecutionStatus.NOT_RAN
188+
return self.result, None
189+
186190
dmesg_content = self._get_dmesg_content()
187191
if args.collect_rotated_logs:
188192
self._collect_dmesg_rotations()

nodescraper/plugins/inband/kernel/kernel_collector.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def collect_data(
5353
"="
5454
)[1]
5555
else:
56-
res = self._run_sut_cmd("sh -c 'uname -r'", sudo=True)
56+
res = self._run_sut_cmd("sh -c 'uname -r'")
5757
if res.exit_code == 0:
5858
kernel = res.stdout
5959

nodescraper/plugins/inband/os/os_collector.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ def collect_data(self, args=None) -> tuple[TaskResult, OsDataModel | None]:
8484
else:
8585
PRETTY_STR = "PRETTY_NAME" # noqa: N806
8686
res = self._run_sut_cmd(
87-
f"sh -c '( lsb_release -ds || (cat /etc/*release | grep {PRETTY_STR}) || uname -om ) 2>/dev/null | head -n1'",
88-
sudo=False,
87+
f"sh -c '( lsb_release -ds || (cat /etc/*release | grep {PRETTY_STR}) || uname -om ) 2>/dev/null | head -n1'"
8988
)
9089
# search for PRETTY_NAME in res
9190
if res.exit_code == 0:

nodescraper/plugins/inband/rocm/rocm_collector.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,34 @@ def collect_data(self, args=None) -> tuple[TaskResult, RocmDataModel | None]:
4343
Returns:
4444
tuple[TaskResult, RocmDataModel | None]: tuple containing the task result and ROCm data model if available.
4545
"""
46-
res = self._run_sut_cmd("cat /opt/rocm/.info/version")
47-
if res.exit_code == 0:
48-
rocm_data = RocmDataModel(rocm_version=res.stdout)
46+
version_paths = [
47+
"/opt/rocm/.info/version-rocm",
48+
"/opt/rocm/.info/version",
49+
]
50+
51+
rocm_data = None
52+
for path in version_paths:
53+
res = self._run_sut_cmd(f"grep . {path}")
54+
if res.exit_code == 0:
55+
rocm_data = RocmDataModel(rocm_version=res.stdout)
56+
self._log_event(
57+
category="ROCM_VERSION_READ",
58+
description="ROCm version data collected",
59+
data=rocm_data.model_dump(),
60+
priority=EventPriority.INFO,
61+
)
62+
self.result.message = f"ROCm: {rocm_data.model_dump()}"
63+
self.result.status = ExecutionStatus.OK
64+
break
65+
else:
4966
self._log_event(
50-
category="ROCM_VERSION_READ",
51-
description="ROCm version data collected",
52-
data=rocm_data.model_dump(),
53-
priority=EventPriority.INFO,
67+
category=EventCategory.OS,
68+
description=f"Unable to read ROCm version from {version_paths}",
69+
data={"raw_output": res.stdout},
70+
priority=EventPriority.ERROR,
5471
)
55-
self.result.message = f"ROCm: {rocm_data.model_dump()}"
56-
self.result.status = ExecutionStatus.OK
57-
else:
58-
rocm_data = None
72+
73+
if not rocm_data:
5974
self._log_event(
6075
category=EventCategory.OS,
6176
description="Error checking ROCm version",

nodescraper/plugins/inband/rocm/rocmdata.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,6 @@ def validate_rocm_version(cls, rocm_version: str) -> str:
4848
Returns:
4949
str: The validated ROCm version string.
5050
"""
51-
if not bool(
52-
re.match(
53-
r"^(\d+(?:\.\d+){0,3})-(\d+)$",
54-
rocm_version,
55-
)
56-
):
51+
if not re.match(r"^\d+(?:\.\d+){0,3}(-\d+)?$", rocm_version):
5752
raise ValueError(f"ROCm version has invalid format: {rocm_version}")
58-
5953
return rocm_version

0 commit comments

Comments
 (0)