Skip to content

Commit

Permalink
feat(anta): Added test case to verify dynamic vlans source (#978)
Browse files Browse the repository at this point in the history
* Issue_864:Added test case to verify dynamic vlans source

* Issue_864: Remove duplicate unit testcases

* Issue_864:Added check for dynamic vlan source exact match and updated UT

* Issue_864: Formatted test description for mkdocs

* Issue_864: Updated docstrings and variable name

* Issue_864: Updated Test failure messages and TC description

* Issue_864: Updated test.yaml file

* Issue_864: Removed duplucate testcases

* Issue_864: Updated docstring and the testcase logic

* Issue_864: Updated test failure message

* Updated test failure messages and docstrings

* Updated test failures messages and testcases

* Update code logic to reflect docstring

---------

Co-authored-by: Geetanjali Mane <geetanjali.mane-ext@Geetanjalis-MacBook-Pro.local>
Co-authored-by: Carl Baillargeon <carl.baillargeon@arista.com>
  • Loading branch information
3 people authored Jan 14, 2025
1 parent 8e5de9a commit c91193e
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 2 deletions.
1 change: 1 addition & 0 deletions anta/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,4 @@ def validate_regex(value: str) -> str:
SnmpVersion = Literal["v1", "v2c", "v3"]
SnmpHashingAlgorithm = Literal["MD5", "SHA", "SHA-224", "SHA-256", "SHA-384", "SHA-512"]
SnmpEncryptionAlgorithm = Literal["AES-128", "AES-192", "AES-256", "DES"]
DynamicVlanSource = Literal["dmf", "dot1x", "dynvtep", "evpn", "mlag", "mlagsync", "mvpn", "swfwd", "vccbfd"]
76 changes: 75 additions & 1 deletion anta/tests/vlan.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from typing import TYPE_CHECKING, ClassVar, Literal

from anta.custom_types import Vlan
from anta.custom_types import DynamicVlanSource, Vlan
from anta.models import AntaCommand, AntaTest
from anta.tools import get_failed_logs, get_value

Expand Down Expand Up @@ -68,3 +68,77 @@ def test(self) -> None:
self.result.is_failure(failed_log)
else:
self.result.is_success()


class VerifyDynamicVlanSource(AntaTest):
"""Verifies dynamic VLAN allocation for specified VLAN sources.
This test performs the following checks for each specified VLAN source:
1. Validates source exists in dynamic VLAN table.
2. Verifies at least one VLAN is allocated to the source.
3. When strict mode is enabled (`strict: true`), ensures no other sources have VLANs allocated.
Expected Results
----------------
* Success: The test will pass if all of the following conditions are met:
- Each specified source exists in dynamic VLAN table.
- Each specified source has at least one VLAN allocated.
- In strict mode: No other sources have VLANs allocated.
* Failure: The test will fail if any of the following conditions is met:
- Specified source not found in configuration.
- Source exists but has no VLANs allocated.
- In strict mode: Non-specified sources have VLANs allocated.
Examples
--------
```yaml
anta.tests.vlan:
- VerifyDynamicVlanSource:
sources:
- evpn
- mlagsync
strict: False
```
"""

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

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

sources: list[DynamicVlanSource]
"""The dynamic VLAN source list."""
strict: bool = False
"""If True, only specified sources are allowed to have VLANs allocated. Default is False."""

@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifyDynamicVlanSource."""
self.result.is_success()
command_output = self.instance_commands[0].json_output
dynamic_vlans = command_output.get("dynamicVlans", {})

# Get all configured sources and sources with VLANs allocated
configured_sources = set(dynamic_vlans.keys())
sources_with_vlans = {source for source, data in dynamic_vlans.items() if data.get("vlanIds")}
expected_sources = set(self.inputs.sources)

# Check if all specified sources exist in configuration
missing_sources = expected_sources - configured_sources
if missing_sources:
self.result.is_failure(f"Dynamic VLAN source(s) not found in configuration: {', '.join(sorted(missing_sources))}")
return

# Check if configured sources have VLANs allocated
sources_without_vlans = expected_sources - sources_with_vlans
if sources_without_vlans:
self.result.is_failure(f"Dynamic VLAN source(s) exist but have no VLANs allocated: {', '.join(sorted(sources_without_vlans))}")
return

# In strict mode, verify no other sources have VLANs allocated
if self.inputs.strict:
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))}")
6 changes: 6 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,12 @@ anta.tests.system:
# Verifies the device uptime.
minimum: 86400
anta.tests.vlan:
- VerifyDynamicVlanSource:
# Verifies dynamic VLAN allocation for specified VLAN sources.
sources:
- evpn
- mlagsync
strict: False
- VerifyVlanInternalPolicy:
# Verifies the VLAN internal allocation policy and the range of VLANs.
policy: ascending
Expand Down
50 changes: 49 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 VerifyVlanInternalPolicy
from anta.tests.vlan import VerifyDynamicVlanSource, VerifyVlanInternalPolicy
from tests.units.anta_tests import test

DATA: list[dict[str, Any]] = [
Expand All @@ -33,4 +33,52 @@
],
},
},
{
"name": "success",
"test": VerifyDynamicVlanSource,
"eos_data": [{"dynamicVlans": {"evpn": {"vlanIds": [1199]}, "mlagsync": {"vlanIds": [1401]}, "vccbfd": {"vlanIds": [1501]}}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": False},
"expected": {"result": "success"},
},
{
"name": "failure-no-dynamic-vlan-sources",
"test": VerifyDynamicVlanSource,
"eos_data": [{"dynamicVlans": {}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": False},
"expected": {"result": "failure", "messages": ["Dynamic VLAN source(s) not found in configuration: evpn, mlagsync"]},
},
{
"name": "failure-dynamic-vlan-sources-mismatch",
"test": VerifyDynamicVlanSource,
"eos_data": [{"dynamicVlans": {"vccbfd": {"vlanIds": [1500]}, "mlagsync": {"vlanIds": [1501]}}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": False},
"expected": {
"result": "failure",
"messages": ["Dynamic VLAN source(s) not found in configuration: evpn"],
},
},
{
"name": "success-strict-mode",
"test": VerifyDynamicVlanSource,
"eos_data": [{"dynamicVlans": {"evpn": {"vlanIds": [1199]}, "mlagsync": {"vlanIds": [1502], "vccbfd": {"vlanIds": []}}}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": True},
"expected": {"result": "success"},
},
{
"name": "failure-all-sources-exact-match-additional-source-found",
"test": VerifyDynamicVlanSource,
"eos_data": [{"dynamicVlans": {"evpn": {"vlanIds": [1199]}, "mlagsync": {"vlanIds": [1500]}, "vccbfd": {"vlanIds": [1500]}}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": True},
"expected": {
"result": "failure",
"messages": ["Strict mode enabled: Unexpected sources have VLANs allocated: vccbfd"],
},
},
{
"name": "failure-all-sources-exact-match-expected-source-not-found",
"test": VerifyDynamicVlanSource,
"eos_data": [{"dynamicVlans": {"evpn": {"vlanIds": [1199]}, "mlagsync": {"vlanIds": []}}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": True},
"expected": {"result": "failure", "messages": ["Dynamic VLAN source(s) exist but have no VLANs allocated: mlagsync"]},
},
]

0 comments on commit c91193e

Please sign in to comment.