diff --git a/anta/tests/stp.py b/anta/tests/stp.py index 93a0d2e39..7a625ff8e 100644 --- a/anta/tests/stp.py +++ b/anta/tests/stp.py @@ -309,3 +309,67 @@ def test(self) -> None: 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() + + +class VerifySTPDisabledVlans(AntaTest): + """Verifies the STP disabled VLAN(s). + + This test performs the following checks: + + 1. Verifies that the STP is configured. + 2. Verifies that the specified VLAN(s) exist on the device. + 3. Verifies that the STP is disabled for the specified VLAN(s). + + Expected Results + ---------------- + * Success: The test will pass if all of the following conditions are met: + - STP is properly configured on the device. + - The specified VLAN(s) exist on the device. + - STP is confirmed to be disabled for all the specified VLAN(s). + * Failure: The test will fail if any of the following condition is met: + - STP is not configured on the device. + - The specified VLAN(s) do not exist on the device. + - STP is enabled for any of the specified VLAN(s). + + Examples + -------- + ```yaml + anta.tests.stp: + - VerifySTPDisabledVlans: + vlans: + - 6 + - 4094 + ``` + """ + + categories: ClassVar[list[str]] = ["stp"] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show spanning-tree vlan detail", revision=1)] + + class Input(AntaTest.Input): + """Input model for the VerifySTPDisabledVlans test.""" + + vlans: list[Vlan] + """List of STP disabled VLAN(s).""" + + @AntaTest.anta_test + def test(self) -> None: + """Main test function for VerifySTPDisabledVlans.""" + self.result.is_success() + + command_output = self.instance_commands[0].json_output + stp_vlan_instances = command_output.get("spanningTreeVlanInstances", {}) + + # If the spanningTreeVlanInstances detail are not found in the command output, the test fails. + if not stp_vlan_instances: + self.result.is_failure("STP is not configured") + return + + actual_vlans = list(stp_vlan_instances) + # If the specified VLAN is not present on the device, STP is enabled for the VLAN(s), test fails. + for vlan in self.inputs.vlans: + if str(vlan) not in actual_vlans: + self.result.is_failure(f"VLAN: {vlan} - Not configured") + continue + + if stp_vlan_instances.get(str(vlan)): + self.result.is_failure(f"VLAN: {vlan} - STP is enabled") diff --git a/examples/tests.yaml b/examples/tests.yaml index e22acf49a..6e1fc5b85 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -749,6 +749,11 @@ anta.tests.stp: # Verifies there is no STP blocked ports. - VerifySTPCounters: # Verifies there is no errors in STP BPDU packets. + - VerifySTPDisabledVlans: + # Verifies the STP disabled VLAN(s). + vlans: + - 6 + - 4094 - VerifySTPForwardingPorts: # Verifies that all interfaces are forwarding for a provided list of VLAN(s). vlans: diff --git a/tests/units/anta_tests/test_stp.py b/tests/units/anta_tests/test_stp.py index 37422108b..bcd83f11d 100644 --- a/tests/units/anta_tests/test_stp.py +++ b/tests/units/anta_tests/test_stp.py @@ -7,7 +7,15 @@ from typing import Any -from anta.tests.stp import VerifySTPBlockedPorts, VerifySTPCounters, VerifySTPForwardingPorts, VerifySTPMode, VerifySTPRootPriority, VerifyStpTopologyChanges +from anta.tests.stp import ( + VerifySTPBlockedPorts, + VerifySTPCounters, + VerifySTPDisabledVlans, + VerifySTPForwardingPorts, + VerifySTPMode, + VerifySTPRootPriority, + VerifyStpTopologyChanges, +) from tests.units.anta_tests import test DATA: list[dict[str, Any]] = [ @@ -486,4 +494,48 @@ "inputs": {"threshold": 10}, "expected": {"result": "failure", "messages": ["STP is not configured."]}, }, + { + "name": "success", + "test": VerifySTPDisabledVlans, + "eos_data": [{"spanningTreeVlanInstances": {"1": {"spanningTreeVlanInstance": {"protocol": "mstp", "bridge": {"priority": 32768}}}, "6": {}, "4094": {}}}], + "inputs": {"vlans": ["6", "4094"]}, + "expected": {"result": "success"}, + }, + { + "name": "failure-stp-not-configured", + "test": VerifySTPDisabledVlans, + "eos_data": [{"spanningTreeVlanInstances": {}}], + "inputs": {"vlans": ["6", "4094"]}, + "expected": {"result": "failure", "messages": ["STP is not configured"]}, + }, + { + "name": "failure-vlans-not-found", + "test": VerifySTPDisabledVlans, + "eos_data": [ + { + "spanningTreeVlanInstances": { + "1": {"spanningTreeVlanInstance": {"protocol": "mstp", "bridge": {}}}, + "6": {"spanningTreeVlanInstance": {"protocol": "mstp", "bridge": {}}}, + "4094": {"spanningTreeVlanInstance": {"protocol": "mstp", "bridge": {}}}, + } + } + ], + "inputs": {"vlans": ["16", "4093"]}, + "expected": {"result": "failure", "messages": ["VLAN: 16 - Not configured", "VLAN: 4093 - Not configured"]}, + }, + { + "name": "failure-vlans-enabled", + "test": VerifySTPDisabledVlans, + "eos_data": [ + { + "spanningTreeVlanInstances": { + "1": {"spanningTreeVlanInstance": {"protocol": "mstp", "bridge": {}}}, + "6": {"spanningTreeVlanInstance": {"protocol": "mstp", "bridge": {}}}, + "4094": {"spanningTreeVlanInstance": {"protocol": "mstp", "bridge": {}}}, + } + } + ], + "inputs": {"vlans": ["6", "4094"]}, + "expected": {"result": "failure", "messages": ["VLAN: 6 - STP is enabled", "VLAN: 4094 - STP is enabled"]}, + }, ]