Skip to content

Commit

Permalink
feat(anta): Added test case to verify Spanning Tree (RPVST) state(sta…
Browse files Browse the repository at this point in the history
…ble topology) (#791)
  • Loading branch information
vitthalmagadum authored Sep 26, 2024
1 parent 0c6b66c commit 6923396
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 2 deletions.
63 changes: 62 additions & 1 deletion anta/tests/stp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# mypy: disable-error-code=attr-defined
from __future__ import annotations

from typing import ClassVar, Literal
from typing import Any, ClassVar, Literal

from pydantic import Field

Expand Down Expand Up @@ -259,3 +259,64 @@ def test(self) -> None:
self.result.is_failure(f"The following instance(s) have the wrong STP root priority configured: {wrong_priority_instances}")
else:
self.result.is_success()


class VerifyStpTopologyChanges(AntaTest):
"""Verifies the number of changes across all interfaces in the Spanning Tree Protocol (STP) topology is below a threshold.
Expected Results
----------------
* Success: The test will pass if the total number of changes across all interfaces is less than the specified threshold.
* Failure: The test will fail if the total number of changes across all interfaces meets or exceeds the specified threshold,
indicating potential instability in the topology.
Examples
--------
```yaml
anta.tests.stp:
- VerifyStpTopologyChanges:
threshold: 10
```
"""

name = "VerifyStpTopologyChanges"
description = "Verifies the number of changes across all interfaces in the Spanning Tree Protocol (STP) topology is below a threshold."
categories: ClassVar[list[str]] = ["stp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show spanning-tree topology status detail", revision=1)]

class Input(AntaTest.Input):
"""Input model for the VerifyStpTopologyChanges test."""

threshold: int
"""The threshold number of changes in the STP topology."""

@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifyStpTopologyChanges."""
failures: dict[str, Any] = {"topologies": {}}

command_output = self.instance_commands[0].json_output
stp_topologies = command_output.get("topologies", {})

# verifies all available topologies except the "NoStp" topology.
stp_topologies.pop("NoStp", None)

# Verify the STP topology(s).
if not stp_topologies:
self.result.is_failure("STP is not configured.")
return

# Verifies the number of changes across all interfaces
for topology, topology_details in stp_topologies.items():
interfaces = {
interface: {"Number of changes": num_of_changes}
for interface, details in topology_details.get("interfaces", {}).items()
if (num_of_changes := details.get("numChanges")) > self.inputs.threshold
}
if interfaces:
failures["topologies"][topology] = interfaces

if failures["topologies"]:
self.result.is_failure(f"The following STP topologies are not configured or number of changes not within the threshold:\n{failures}")
else:
self.result.is_success()
2 changes: 2 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ anta.tests.stp:
instances:
- 10
- 20
- VerifyStpTopologyChanges:
threshold: 10

anta.tests.stun:
- VerifyStunClient:
Expand Down
164 changes: 163 additions & 1 deletion tests/units/anta_tests/test_stp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Any

from anta.tests.stp import VerifySTPBlockedPorts, VerifySTPCounters, VerifySTPForwardingPorts, VerifySTPMode, VerifySTPRootPriority
from anta.tests.stp import VerifySTPBlockedPorts, VerifySTPCounters, VerifySTPForwardingPorts, VerifySTPMode, VerifySTPRootPriority, VerifyStpTopologyChanges
from tests.units.anta_tests import test

DATA: list[dict[str, Any]] = [
Expand Down Expand Up @@ -324,4 +324,166 @@
"inputs": {"priority": 32768, "instances": [10, 20, 30]},
"expected": {"result": "failure", "messages": ["The following instance(s) have the wrong STP root priority configured: ['VL20', 'VL30']"]},
},
{
"name": "success-mstp",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"Cist": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.7353542},
}
},
"NoStp": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"Ethernet1": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
},
},
},
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
},
{
"name": "success-rstp",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"Cist": {
"interfaces": {
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"PeerEthernet3": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.7353542},
}
},
"NoStp": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"Ethernet1": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
},
},
},
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
},
{
"name": "success-rapid-pvst",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"NoStp": {
"vlans": [4094, 4093, 1006],
"interfaces": {
"PeerEthernet2": {"state": "forwarding", "numChanges": 1, "lastChange": 1727151356.1330667},
},
},
"Vl1": {"vlans": [1], "interfaces": {"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0615358}}},
"Vl10": {
"vlans": [10],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0673406},
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0677001},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0728855},
"Ethernet3": {"state": "forwarding", "numChanges": 3, "lastChange": 1727326730.255137},
},
},
"Vl1198": {
"vlans": [1198],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.074386},
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0743902},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0743942},
},
},
"Vl1199": {
"vlans": [1199],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0744},
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.07453},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.074535},
},
},
"Vl20": {
"vlans": [20],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.073489},
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0743747},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0743794},
"Ethernet3": {"state": "forwarding", "numChanges": 3, "lastChange": 1727326730.2551405},
},
},
"Vl3009": {
"vlans": [3009],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.074541},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0745454},
},
},
"Vl3019": {
"vlans": [3019],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0745502},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0745537},
},
},
},
},
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
},
{
"name": "failure-unstable-topology",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"Cist": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.735365},
"Port-Channel5": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
},
},
},
],
"inputs": {"threshold": 10},
"expected": {
"result": "failure",
"messages": [
"The following STP topologies are not configured or number of changes not within the threshold:\n"
"{'topologies': {'Cist': {'Cpu': {'Number of changes': 15}, 'Port-Channel5': {'Number of changes': 15}}}}"
],
},
},
{
"name": "failure-topologies-not-configured",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"NoStp": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"Ethernet1": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
}
},
},
],
"inputs": {"threshold": 10},
"expected": {"result": "failure", "messages": ["STP is not configured."]},
},
]

0 comments on commit 6923396

Please sign in to comment.