8000 feat(anta.tests): Added the test case to verify the vlan status by vitthalmagadum · Pull Request #1108 · aristanetworks/anta · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(anta.tests): Added the test case to verify the vlan status #1108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion anta/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def update_bgp_redistributed_proto_user(value: str) -> str:

# AntaTest.Input types
AAAAuthMethod = Annotated[str, AfterValidator(aaa_group_prefix)]
Vlan = Annotated[int, Field(ge=0, le=4094)]
VlanId = Annotated[int, Field(ge=0, le=4094)]
MlagPriority = Annotated[int, Field(ge=1, le=32767)]
Vni = Annotated[int, Field(ge=1, le=16777215)]
Interface = Annotated[
Expand Down
26 changes: 26 additions & 0 deletions anta/input_models/vlan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (c) 2023-2025 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
"""Module containing input models for VLAN tests."""

from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, ConfigDict

from anta.custom_types import VlanId


class Vlan(BaseModel):
"""Model for a VLAN."""

model_config = ConfigDict(extra="forbid")
vlan_id: VlanId
"""The VLAN ID."""
status: Literal["active", "suspended", "inactive"]
"""The VLAN administrative status."""

def __str__(self) -> str:
"""Representation of the VLAN model."""
return f"VLAN: Vlan{self.vlan_id}"
4 changes: 2 additions & 2 deletions anta/tests/multicast.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from typing import TYPE_CHECKING, ClassVar

from anta.custom_types import Vlan
from anta.custom_types import VlanId
from anta.models import AntaCommand, AntaTest

if TYPE_CHECKING:
Expand Down Expand Up @@ -41,7 +41,7 @@ class VerifyIGMPSnoopingVlans(AntaTest):
class Input(AntaTest.Input):
"""Input model for the VerifyIGMPSnoopingVlans test."""

vlans: dict[Vlan, bool]
vlans: dict[VlanId, bool]
"""Dictionary with VLAN ID and whether IGMP snooping must be enabled (True) or disabled (False)."""

@AntaTest.anta_test
Expand Down
10 changes: 5 additions & 5 deletions anta/tests/stp.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from pydantic import Field

from anta.custom_types import Vlan
from anta.custom_types import VlanId
from anta.models import AntaCommand, AntaTemplate, AntaTest
from anta.tools import get_value

Expand Down Expand Up @@ -44,7 +44,7 @@ class Input(AntaTest.Input):

mode: Literal["mstp", "rstp", "rapidPvst"] = "mstp"
"""STP mode to verify. Supported values: mstp, rstp, rapidPvst. Defaults to mstp."""
vlans: list[Vlan]
vlans: list[VlanId]
"""List of VLAN on which to verify STP mode."""

def render(self, template: AntaTemplate) -> list[AntaCommand]:
Expand Down Expand Up @@ -157,7 +157,7 @@ class VerifySTPForwardingPorts(AntaTest):
class Input(AntaTest.Input):
"""Input model for the VerifySTPForwardingPorts test."""

vlans: list[Vlan]
vlans: list[VlanId]
"""List of VLAN on which to verify forwarding states."""

def render(self, template: AntaTemplate) -> list[AntaCommand]:
Expand Down Expand Up @@ -213,7 +213,7 @@ class Input(AntaTest.Input):

priority: int
"""STP root priority to verify."""
instances: list[Vlan] = Field(default=[])
instances: list[VlanId] = Field(default=[])
"""List of VLAN or MST instance ID(s). If empty, ALL VLAN or MST instance ID(s) will be verified."""

@AntaTest.anta_test
Expand Down Expand Up @@ -331,7 +331,7 @@ class VerifySTPDisabledVlans(AntaTest):
class Input(AntaTest.Input):
"""Input model for the VerifySTPDisabledVlans test."""

vlans: list[Vlan]
vlans: list[VlanId]
"""List of STP disabled VLAN(s)."""

@AntaTest.anta_test
Expand Down
52 changes: 49 additions & 3 deletions anta/tests/vlan.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

from typing import TYPE_CHECKING, ClassVar, Literal

from anta.custom_types import DynamicVlanSource, Vlan
from anta.custom_types import DynamicVlanSource, VlanId
from anta.input_models.vlan import Vlan
from anta.models import AntaCommand, AntaTest
from anta.tools import get_value

Expand Down Expand Up @@ -47,9 +48,9 @@ class Input(AntaTest.Input):

policy: Literal["ascending", "descending"]
"""The VLAN internal allocation policy. Supported values: ascending, descending."""
start_vlan_id: Vlan
start_vlan_id: VlanId
"""The starting VLAN ID in the range."""
end_vlan_id: Vlan
end_vlan_id: VlanId
"""The ending VLAN ID in the range."""

@AntaTest.anta_test
Expand Down Expand Up @@ -145,3 +146,48 @@ def test(self) -> None:
unexpected_sources = sources_with_vlans - expected_sources
if unexpected_sources:
self.result.is_failure(f"Strict mode enabled: Unexpected sources have VLANs allocated: {', '.join(sorted(unexpected_sources))}")


class VerifyVlanStatus(AntaTest):
"""Verifies the administrative status of specified VLANs.

Expected Results
----------------
* Success: The test will pass if all specified VLANs exist in the configuration and their administrative status is correct.
* Failure: The test will fail if any of the specified VLANs is not found in the configuration or if its administrative status is incorrect.

Examples
--------
```yaml
anta.tests.vlan:
- VerifyVlanStatus:
vlans:
- vlan_id: 10
status: suspended
- vlan_id: 4094
status: active
```
"""

categories: ClassVar[list[str]] = ["vlan"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show vlan", revision=1)]

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

vlans: list[Vlan]
"""List of VLAN details."""

@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifyVlanStatus."""
self.result.is_success()
command_output = self.instance_commands[0].json_output

for vlan in self.inputs.vlans:
if (vlan_detail := get_value(command_output, f"vlans.{vlan.vlan_id}")) is None:
self.result.is_failure(f"{vlan} - Not configured")
continue

if (act_status := vlan_detail["status"]) != vlan.status:
self.result.is_failure(f"{vlan} - Incorrect administrative status - Expected: {vlan.status} Actual: {act_status}")
4 changes: 2 additions & 2 deletions anta/tests/vxlan.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from pydantic import Field

from anta.custom_types import Vlan, Vni, VxlanSrcIntf
from anta.custom_types import VlanId, Vni, VxlanSrcIntf
from anta.models import AntaCommand, AntaTest
from anta.tools import get_value

Expand Down Expand Up @@ -127,7 +127,7 @@ class VerifyVxlanVniBinding(AntaTest):
class Input(AntaTest.Input):
"""Input model for the VerifyVxlanVniBinding test."""

bindings: dict[Vni, Vlan]
bindings: dict[Vni, VlanId]
"""VNI to VLAN bindings to verify."""

@AntaTest.anta_test
Expand Down
18 changes: 18 additions & 0 deletions docs/api/tests/vlan.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ anta_title: ANTA catalog for VLAN tests
~ that can be found in the LICENSE file.
-->

# Tests

::: anta.tests.vlan

options:
Expand All @@ -22,3 +24,19 @@ anta_title: ANTA catalog for VLAN tests
show_root_toc_entry: false
show_symbol_type_heading: false
show_symbol_type_toc: false

# Input models

::: anta.input_models.vlan

options:
anta_hide_test_module_description: true
filters:
- "!^__str__"
merge_init_into_class: false
show_bases: false
show_labels: true
show_root_heading: false
show_root_toc_entry: false
show_symbol_type_heading: false
show_symbol_type_toc: false
7 changes: 7 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,13 @@ anta.tests.vlan:
policy: ascending
start_vlan_id: 1006
end_vlan_id: 4094
- VerifyVlanStatus:
# Verifies the administrative status of specified VLANs.
vlans:
- vlan_id: 10
status: suspended
- vlan_id: 4094
status: active
anta.tests.vxlan:
- VerifyVxlan1ConnSettings:
# Verifies Vxlan1 source interface and UDP port.
Expand Down
60 changes: 59 additions & 1 deletion tests/units/anta_tests/test_vlan.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Any

from anta.tests.vlan import VerifyDynamicVlanSource, VerifyVlanInternalPolicy
from anta.tests.vlan import VerifyDynamicVlanSource, VerifyVlanInternalPolicy, VerifyVlanStatus
from tests.units.anta_tests import test

DATA: list[dict[str, Any]] = [
Expand Down Expand Up @@ -89,4 +89,62 @@
"inputs": {"sources": ["evpn", "mlagsync"], "strict": True},
"expected": {"result": "failure", "messages": ["Dynamic VLAN source(s) exist but have no VLANs allocated: mlagsync"]},
},
{
"name": "success",
"test": VerifyVlanStatus,
"eos_data": [
{
"vlans": {
"1": {"name": "default", "dynamic": False, "status": "active", "interfaces": {}},
"4092": {"name": "VLAN4092", "dynamic": True, "status": "active", "interfaces": {}},
"4094": {"name": "VLAN4094", "dynamic": True, "status": "active", "interfaces": {}},
},
"sourceDetail": "",
}
],
"inputs": {"vlans": [{"vlan_id": 4092, "status": "active"}, {"vlan_id": 4094, "status": "active"}]},
"expected": {"result": "success"},
},
{
"name": "failure-vlan-not-conifgured",
"test": VerifyVlanStatus,
"eos_data": [
{
"vlans": {
"1": {"name": "default", "dynamic": False, "status": "active", "interfaces": {}},
},
"sourceDetail": "",
}
],
"inputs": {"vlans": [{"vlan_id": 4092, "status": "active"}, {"vlan_id": 4094, "status": "active"}]},
"expected": {
"result": "failure",
"messages": [
"VLAN: Vlan4092 - Not configured",
"VLAN: Vlan4094 - Not configured",
],
},
},
{
"name": "failure-incorrect-status",
"test": VerifyVlanStatus,
"eos_data": [
{
"vlans": {
"1": {"name": "default", "dynamic": False, "status": "active", "interfaces": {}},
"4092": {"name": "VLAN4092", "dynamic": True, "status": "suspended", "interfaces": {}},
"4094": {"name": "VLAN4094", "dynamic": True, "status": "active", "interfaces": {}},
},
"sourceDetail": "",
}
],
"inputs": {"vlans": [{"vlan_id": 4092, "status": "active"}, {"vlan_id": 4094, "status": "suspended"}]},
"expected": {
"result": "failure",
"messages": [
"VLAN: Vlan4092 - Incorrect administrative status - Expected: active Actual: suspended",
"VLAN: Vlan4094 - Incorrect administrative status - Expected: suspended Actual: active",
],
},
},
]
0