Skip to content
11 changes: 10 additions & 1 deletion nodescraper/interfaces/dataplugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,16 @@ def collect(
):
self.connection_manager.connect()

if self.connection_manager.result.status != ExecutionStatus.OK:
# Proceed with collection as long as a usable connection was
# established. A non-fatal connection warning (e.g. the SUT OS
# could not be determined, as happens with switch CLIs that do not
# support ``uname``) must not block collection: each collector
# enforces its own ``SUPPORTED_OS_FAMILY`` and will be skipped via
# ``SystemCompatibilityError`` if the OS is unsupported.
Comment thread
sunnyhe2 marked this conversation as resolved.
Outdated
if (
self.connection_manager.connection is None
or self.connection_manager.result.status >= ExecutionStatus.ERROR
):
self.collection_result = TaskResult(
task=primary_collector.__name__,
parent=self.__class__.__name__,
Expand Down
29 changes: 29 additions & 0 deletions nodescraper/plugins/inband/switch/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2026 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 .scale_out_arista import ScaleOutAristaPlugin
from .scale_out_dell import ScaleOutDellPlugin

__all__ = ["ScaleOutAristaPlugin", "ScaleOutDellPlugin"]
28 changes: 28 additions & 0 deletions nodescraper/plugins/inband/switch/scale_out_arista/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2026 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 .scale_out_arista_plugin import ScaleOutAristaPlugin

__all__ = ["ScaleOutAristaPlugin"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2026 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 List, Optional

from pydantic import Field

from nodescraper.models import AnalyzerArgs


class ScaleOutAristaAnalyzerArgs(AnalyzerArgs):
"""Arguments for the Arista switch analyzer."""

analysis_ports: Optional[List[str]] = Field(
default=None,
description=(
"Restrict per-port analysis to the given ports. Ports specified in"
"the form 'S/P' (e.g. ['1/1', '2/1', '17/1']) "
"When omitted, every port present in the data is "
"analyzed, Independent of any collection-time port filter."
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2026 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 List, Optional

from pydantic import Field

from nodescraper.models import CollectorArgs


class ScaleOutAristaCollectorArgs(CollectorArgs):
"""Arguments for the Arista switch collector."""

collection_ports: Optional[List[str]] = Field(
default=None,
description=(
"Restrict collection to the given port(s). Each list element "
"triggers one command invocation"
"Accepted forms include '1/1', '1/1-8/1'"
"(e.g. ['1/1-3/1', '17/1-17/1']). "
"When omitted, commands run once against all ports."
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2026 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.
#
###############################################################################

import re
from typing import Any, ClassVar

from nodescraper.interfaces import DataAnalyzer

from ..switch_analyzer_base import SwitchAnalyzerBase
from .analyzer_args import ScaleOutAristaAnalyzerArgs
from .scaleoutaristadata import ScaleOutAristaDataModel


class ScaleOutAristaAnalyzer(
SwitchAnalyzerBase[ScaleOutAristaDataModel],
DataAnalyzer[ScaleOutAristaDataModel, ScaleOutAristaAnalyzerArgs],
):
"""Check Arista switch data for errors and warnings.

Walks every model in the collected :class:`ScaleOutAristaDataModel` and checks
each ``error_fields`` / ``warning_fields`` ClassVar against an optional
``ports`` filter.
"""

VENDOR_NAME: ClassVar[str] = "Arista"
DATA_MODEL = ScaleOutAristaDataModel

# ``M/S`` port identifier (e.g. ``1/1``), with optional ``Ethernet``
# prefix so both filter tokens (``"1/1"``) and live port names
# (``"Ethernet1/1"``) normalize to the same canonical key.
PORT_NAME_RE: ClassVar[re.Pattern] = re.compile(r"^(?:Ethernet)?(\d+)/(\d+)$", re.IGNORECASE)
Comment thread
sunnyhe2 marked this conversation as resolved.
Outdated
PORT_FORMAT_HINT: ClassVar[str] = "expected form 'M/S'"

def _walk_system(self, switch_data: ScaleOutAristaDataModel) -> list[dict[str, Any]]:
findings: list[dict[str, Any]] = []

if switch_data.system_env is None:
return findings

findings.extend(
self._check_model(
switch_data.system_env,
context={"section": "system_env"},
)
)

for idx, psu in enumerate(switch_data.system_env.power_supply_slots or []):
findings.extend(
self._check_model(
psu,
context={
"section": "power_supply_slots",
"index": idx,
"label": psu.label,
},
)
)

for idx, fan in enumerate(switch_data.system_env.fan_tray_slots or []):
findings.extend(
self._check_model(
fan,
context={
"section": "fan_tray_slots",
"index": idx,
"label": fan.label,
},
)
)

return findings
Loading
Loading