Skip to content
42 changes: 27 additions & 15 deletions nodescraper/models/taskresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,28 +103,40 @@ def duration(self) -> Optional[str]:
return duration

def _get_event_summary(self) -> str:
"""Get summary string for artifacts
"""Get summary string for events

Returns:
str: artifact summary
str: event summary with counts and descriptions
"""
error_count = 0
warning_count = 0
error_msg_counts: dict[str, int] = {}
warning_msg_counts: dict[str, int] = {}

for event in self.events:
if event.priority == EventPriority.WARNING:
warning_count += 1
warning_msg_counts[event.description] = (
warning_msg_counts.get(event.description, 0) + 1
)
elif event.priority >= EventPriority.ERROR:
error_count += 1

summary_list = []

if warning_count:
summary_list.append(f"{warning_count} warnings")
if error_count:
summary_list.append(f"{error_count} errors")

return "|".join(summary_list)
error_msg_counts[event.description] = error_msg_counts.get(event.description, 0) + 1

summary_parts = []

if warning_msg_counts:
total_warnings = sum(warning_msg_counts.values())
warning_details = [
f"{msg} (x{count})" if count > 1 else msg
for msg, count in warning_msg_counts.items()
]
summary_parts.append(f"{total_warnings} warnings: {', '.join(warning_details)}")

if error_msg_counts:
total_errors = sum(error_msg_counts.values())
error_details = [
f"{msg} (x{count})" if count > 1 else msg for msg, count in error_msg_counts.items()
]
summary_parts.append(f"{total_errors} errors: {', '.join(error_details)}")

return "; ".join(summary_parts)

def _update_status(self) -> None:
"""Update overall status based on event priority"""
Expand Down
29 changes: 29 additions & 0 deletions nodescraper/plugins/inband/pcie/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from .analyzer_args import PcieAnalyzerArgs
from .pcie_plugin import PciePlugin

__all__ = ["PciePlugin", "PcieAnalyzerArgs"]
63 changes: 63 additions & 0 deletions nodescraper/plugins/inband/pcie/analyzer_args.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from typing import Dict, Optional, Union

from nodescraper.models import AnalyzerArgs


class PcieAnalyzerArgs(AnalyzerArgs):
"""Arguments for PCIe analyzer

Attributes:
exp_speed: Expected PCIe speed (generation 1-5)
exp_width: Expected PCIe width (1-16 lanes)
exp_sriov_count: Expected SR-IOV VF count
exp_gpu_count_override: Override expected GPU count
exp_max_payload_size: Expected max payload size (int for all devices, dict for specific device IDs)
exp_max_rd_req_size: Expected max read request size (int for all devices, dict for specific device IDs)
exp_ten_bit_tag_req_en: Expected 10-bit tag request enable (int for all devices, dict for specific device IDs)
"""

exp_speed: int = 5
exp_width: int = 16
exp_sriov_count: int = 0
exp_gpu_count_override: Optional[int] = None
exp_max_payload_size: Optional[Union[Dict[int, int], int]] = None
exp_max_rd_req_size: Optional[Union[Dict[int, int], int]] = None
exp_ten_bit_tag_req_en: Optional[Union[Dict[int, int], int]] = None


def normalize_to_dict(
value: Optional[Union[Dict[int, int], int]], vendorid_ep: int
) -> Dict[int, int]:
"""Normalize int or dict values to dict format using vendorid_ep as key for int values"""
if value is None:
return {}
if isinstance(value, int):
return {vendorid_ep: value}
if isinstance(value, dict):
return value
return {}
Loading