Merging upstream version 1.4.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-15 09:34:30 +02:00
parent a6f5a146cb
commit 3254dea030
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
166 changed files with 13787 additions and 11959 deletions

View file

@ -3,30 +3,77 @@
# that can be found in the LICENSE file.
"""Tests for anta.tests module."""
from __future__ import annotations
import asyncio
from typing import Any
import sys
from typing import TYPE_CHECKING, Any, Literal, TypedDict
from anta.device import AntaDevice
from anta.models import AntaTest
if TYPE_CHECKING:
from anta.device import AntaDevice
from anta.result_manager.models import AntaTestStatus
if sys.version_info >= (3, 11):
from typing import NotRequired
else:
from typing_extensions import NotRequired
def test(device: AntaDevice, data: dict[str, Any]) -> None:
class UnitTestResult(TypedDict):
"""Expected result of a unit test of an AntaTest subclass.
For our AntaTest unit tests we expect only success, failure or skipped.
Never unset nor error.
"""
result: Literal[AntaTestStatus.SUCCESS, AntaTestStatus.FAILURE, AntaTestStatus.SKIPPED]
messages: NotRequired[list[str]]
class AntaUnitTest(TypedDict):
"""The parameters required for a unit test of an AntaTest subclass."""
inputs: NotRequired[dict[str, Any]]
eos_data: list[dict[str, Any] | str]
expected: UnitTestResult
if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
TypeAlias = type
AntaUnitTestDataDict: TypeAlias = dict[tuple[type[AntaTest], str], AntaUnitTest]
def test(device: AntaDevice, data: tuple[tuple[type[AntaTest], str], AntaUnitTest]) -> None:
"""Generic test function for AntaTest subclass.
Generate unit tests for each AntaTest subclass.
See `tests/units/anta_tests/README.md` for more information on how to use it.
"""
# Extract the test class, name and test data from a nested tuple structure:
# `val: Tuple[Tuple[Type[AntaTest], str], AntaUnitTest]`
(anta_test, name), test_data = data
# Instantiate the AntaTest subclass
test_instance = data["test"](device, inputs=data["inputs"], eos_data=data["eos_data"])
test_instance = anta_test(device, inputs=test_data.get("inputs"), eos_data=test_data["eos_data"])
# Run the test() method
asyncio.run(test_instance.test())
# Assert expected result
assert test_instance.result.result == data["expected"]["result"], f"Expected '{data['expected']['result']}' result, got '{test_instance.result.result}'"
if "messages" in data["expected"]:
assert test_instance.result.result == test_data["expected"]["result"], (
f"Expected '{test_data['expected']['result']}' result, got '{test_instance.result.result}'"
)
if "messages" in test_data["expected"]:
# We expect messages in test result
assert len(test_instance.result.messages) == len(data["expected"]["messages"])
assert len(test_instance.result.messages) == len(test_data["expected"]["messages"])
# Test will pass if the expected message is included in the test result message
for message, expected in zip(test_instance.result.messages, data["expected"]["messages"]): # NOTE: zip(strict=True) has been added in Python 3.10
for message, expected in zip(test_instance.result.messages, test_data["expected"]["messages"]): # NOTE: zip(strict=True) has been added in Python 3.10
assert expected in message
else:
# Test result should not have messages

View file

@ -7,17 +7,27 @@ from typing import Any
import pytest
from anta.models import AntaTest
from tests.units.anta_tests import AntaUnitTest
def build_test_id(val: dict[str, Any]) -> str:
def build_test_id(val: tuple[tuple[type[AntaTest], str], AntaUnitTest]) -> str:
"""Build id for a unit test of an AntaTest subclass.
{
"name": "meaniful test name",
"test": <AntaTest instance>,
(<AntaTest instance>, "meaniful test name"):
{
"eos_data": [{}],
....
}
...
}
"""
return f"{val['test'].__module__}.{val['test'].__name__}-{val['name']}"
# Extract the test class and its name from a nested tuple structure:
# `val: Tuple[Tuple[Type[AntaTest], str], AntaUnitTest]`
(anta_test, test_name) = val[0]
return f"{anta_test.__module__}.{anta_test.__name__}-{test_name}"
def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
@ -32,4 +42,4 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
"""
if "tests.units.anta_tests" in metafunc.module.__package__ and metafunc.function.__name__ == "test":
# This is a unit test for an AntaTest subclass
metafunc.parametrize("data", metafunc.module.DATA, ids=build_test_id)
metafunc.parametrize("data", metafunc.module.DATA.items(), ids=build_test_id)

File diff suppressed because it is too large Load diff

View file

@ -6,73 +6,53 @@
from __future__ import annotations
import sys
from typing import Any
from typing import TYPE_CHECKING, Any
import pytest
from pydantic import ValidationError
from anta.tests.routing.generic import VerifyIPv4RouteNextHops, VerifyIPv4RouteType, VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.routing.generic import (
VerifyIPv4RouteNextHops,
VerifyIPv4RouteType,
VerifyRoutingProtocolModel,
VerifyRoutingStatus,
VerifyRoutingTableEntry,
VerifyRoutingTableSize,
)
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyRoutingProtocolModel,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyRoutingProtocolModel, "success"): {
"eos_data": [{"vrfs": {"default": {}}, "protoModelStatus": {"configuredProtoModel": "multi-agent", "operatingProtoModel": "multi-agent"}}],
"inputs": {"model": "multi-agent"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-wrong-configured-model",
"test": VerifyRoutingProtocolModel,
(VerifyRoutingProtocolModel, "failure-wrong-configured-model"): {
"eos_data": [{"vrfs": {"default": {}}, "protoModelStatus": {"configuredProtoModel": "ribd", "operatingProtoModel": "ribd"}}],
"inputs": {"model": "multi-agent"},
"expected": {"result": "failure", "messages": ["Routing model is misconfigured - Expected: multi-agent Actual: ribd"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Routing model is misconfigured - Expected: multi-agent Actual: ribd"]},
},
{
"name": "failure-mismatch-operating-model",
"test": VerifyRoutingProtocolModel,
(VerifyRoutingProtocolModel, "failure-mismatch-operating-model"): {
"eos_data": [{"vrfs": {"default": {}}, "protoModelStatus": {"configuredProtoModel": "multi-agent", "operatingProtoModel": "ribd"}}],
"inputs": {"model": "multi-agent"},
"expected": {"result": "failure", "messages": ["Routing model is misconfigured - Expected: multi-agent Actual: ribd"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Routing model is misconfigured - Expected: multi-agent Actual: ribd"]},
},
{
"name": "success",
"test": VerifyRoutingTableSize,
"eos_data": [
{
"vrfs": {
"default": {
# Output truncated
"maskLen": {"8": 2},
"totalRoutes": 123,
},
},
},
],
(VerifyRoutingTableSize, "success"): {
"eos_data": [{"vrfs": {"default": {"maskLen": {"8": 2}, "totalRoutes": 123}}}],
"inputs": {"minimum": 42, "maximum": 666},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyRoutingTableSize,
"eos_data": [
{
"vrfs": {
"default": {
# Output truncated
"maskLen": {"8": 2},
"totalRoutes": 1000,
},
},
},
],
(VerifyRoutingTableSize, "failure"): {
"eos_data": [{"vrfs": {"default": {"maskLen": {"8": 2}, "totalRoutes": 1000}}}],
"inputs": {"minimum": 42, "maximum": 666},
"expected": {"result": "failure", "messages": ["Routing table routes are outside the routes range - Expected: 42 <= to >= 666 Actual: 1000"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Routing table routes are outside the routes range - Expected: 42 <= to >= 666 Actual: 1000"]},
},
{
"name": "success",
"test": VerifyRoutingTableEntry,
(VerifyRoutingTableEntry, "success"): {
"eos_data": [
{
"vrfs": {
@ -92,10 +72,10 @@ DATA: list[dict[str, Any]] = [
"preference": 20,
"metric": 0,
"vias": [{"nexthopAddr": "10.1.255.4", "interface": "Ethernet1"}],
},
}
},
},
},
}
}
},
{
"vrfs": {
@ -115,18 +95,16 @@ DATA: list[dict[str, Any]] = [
"preference": 20,
"metric": 0,
"vias": [{"nexthopAddr": "10.1.255.6", "interface": "Ethernet2"}],
},
}
},
},
},
}
}
},
],
"inputs": {"vrf": "default", "routes": ["10.1.0.1", "10.1.0.2"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-collect-all",
"test": VerifyRoutingTableEntry,
(VerifyRoutingTableEntry, "success-collect-all"): {
"eos_data": [
{
"vrfs": {
@ -159,16 +137,14 @@ DATA: list[dict[str, Any]] = [
"vias": [{"nexthopAddr": "10.1.255.6", "interface": "Ethernet2"}],
},
},
},
},
},
}
}
}
],
"inputs": {"vrf": "default", "routes": ["10.1.0.1", "10.1.0.2"], "collect": "all"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-missing-route",
"test": VerifyRoutingTableEntry,
(VerifyRoutingTableEntry, "failure-missing-route"): {
"eos_data": [
{
"vrfs": {
@ -178,8 +154,8 @@ DATA: list[dict[str, Any]] = [
"allRoutesProgrammedKernel": True,
"defaultRouteState": "notSet",
"routes": {},
},
},
}
}
},
{
"vrfs": {
@ -199,10 +175,10 @@ DATA: list[dict[str, Any]] = [
"preference": 20,
"metric": 0,
"vias": [{"nexthopAddr": "10.1.255.6", "interface": "Ethernet2"}],
},
}
},
},
},
}
}
},
{
"vrfs": {
@ -212,16 +188,14 @@ DATA: list[dict[str, Any]] = [
"allRoutesProgrammedKernel": True,
"defaultRouteState": "notSet",
"routes": {},
},
},
}
}
},
],
"inputs": {"vrf": "default", "routes": ["10.1.0.1", "10.1.0.2", "10.1.0.3"]},
"expected": {"result": "failure", "messages": ["The following route(s) are missing from the routing table of VRF default: 10.1.0.1, 10.1.0.3"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["The following route(s) are missing from the routing table of VRF default: 10.1.0.1, 10.1.0.3"]},
},
{
"name": "failure-wrong-route",
"test": VerifyRoutingTableEntry,
(VerifyRoutingTableEntry, "failure-wrong-route"): {
"eos_data": [
{
"vrfs": {
@ -241,10 +215,10 @@ DATA: list[dict[str, Any]] = [
"preference": 20,
"metric": 0,
"vias": [{"nexthopAddr": "10.1.255.4", "interface": "Ethernet1"}],
},
}
},
},
},
}
}
},
{
"vrfs": {
@ -264,18 +238,16 @@ DATA: list[dict[str, Any]] = [
"preference": 20,
"metric": 0,
"vias": [{"nexthopAddr": "10.1.255.6", "interface": "Ethernet2"}],
},
}
},
},
},
}
}
},
],
"inputs": {"vrf": "default", "routes": ["10.1.0.1", "10.1.0.2"]},
"expected": {"result": "failure", "messages": ["The following route(s) are missing from the routing table of VRF default: 10.1.0.2"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["The following route(s) are missing from the routing table of VRF default: 10.1.0.2"]},
},
{
"name": "failure-wrong-route-collect-all",
"test": VerifyRoutingTableEntry,
(VerifyRoutingTableEntry, "failure-wrong-route-collect-all"): {
"eos_data": [
{
"vrfs": {
@ -308,16 +280,14 @@ DATA: list[dict[str, Any]] = [
"vias": [{"nexthopAddr": "10.1.255.6", "interface": "Ethernet2"}],
},
},
},
},
},
}
}
}
],
"inputs": {"vrf": "default", "routes": ["10.1.0.1", "10.1.0.2"], "collect": "all"},
"expected": {"result": "failure", "messages": ["The following route(s) are missing from the routing table of VRF default: 10.1.0.2"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["The following route(s) are missing from the routing table of VRF default: 10.1.0.2"]},
},
{
"name": "success-valid-route-type",
"test": VerifyIPv4RouteType,
(VerifyIPv4RouteType, "success-valid-route-type"): {
"eos_data": [
{
"vrfs": {
@ -333,42 +303,31 @@ DATA: list[dict[str, Any]] = [
{"vrf": "MGMT", "prefix": "10.100.1.5/32", "route_type": "iBGP"},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-route-not-found",
"test": VerifyIPv4RouteType,
(VerifyIPv4RouteType, "failure-route-not-found"): {
"eos_data": [{"vrfs": {"default": {"routes": {}}}}],
"inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}]},
"expected": {"result": "failure", "messages": ["Prefix: 10.10.0.1/32 VRF: default - Route not found"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Prefix: 10.10.0.1/32 VRF: default - Route not found"]},
},
{
"name": "failure-invalid-route-type",
"test": VerifyIPv4RouteType,
(VerifyIPv4RouteType, "failure-invalid-route-type"): {
"eos_data": [{"vrfs": {"default": {"routes": {"10.10.0.1/32": {"routeType": "eBGP"}}}}}],
"inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "iBGP"}]},
"expected": {
"result": "failure",
"messages": ["Prefix: 10.10.0.1/32 VRF: default - Incorrect route type - Expected: iBGP Actual: eBGP"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Prefix: 10.10.0.1/32 VRF: default - Incorrect route type - Expected: iBGP Actual: eBGP"]},
},
{
"name": "failure-vrf-not-configured",
"test": VerifyIPv4RouteType,
(VerifyIPv4RouteType, "failure-vrf-not-configured"): {
"eos_data": [{"vrfs": {}}],
"inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}]},
"expected": {"result": "failure", "messages": ["Prefix: 10.10.0.1/32 VRF: default - VRF not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Prefix: 10.10.0.1/32 VRF: default - VRF not configured"]},
},
{
"name": "success",
"test": VerifyIPv4RouteNextHops,
(VerifyIPv4RouteNextHops, "success"): {
"eos_data": [
{
"vrfs": {
"default": {
"routes": {
"10.10.0.1/32": {
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}],
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}]
}
}
},
@ -379,12 +338,12 @@ DATA: list[dict[str, Any]] = [
{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"},
{"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"},
{"nexthopAddr": "10.100.0.101", "interface": "Ethernet4"},
],
]
}
}
},
}
},
}
],
"inputs": {
"route_entries": [
@ -392,30 +351,28 @@ DATA: list[dict[str, Any]] = [
{"prefix": "10.100.0.128/31", "vrf": "MGMT", "nexthops": ["10.100.0.8", "10.100.0.10"]},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-strict-true",
"test": VerifyIPv4RouteNextHops,
(VerifyIPv4RouteNextHops, "success-strict-true"): {
"eos_data": [
{
"vrfs": {
"default": {
"routes": {
"10.10.0.1/32": {
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}],
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}]
}
}
},
"MGMT": {
"routes": {
"10.100.0.128/31": {
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}],
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}]
}
}
},
}
},
}
],
"inputs": {
"route_entries": [
@ -423,14 +380,10 @@ DATA: list[dict[str, Any]] = [
{"prefix": "10.100.0.128/31", "vrf": "MGMT", "strict": True, "nexthops": ["10.100.0.8", "10.100.0.10"]},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-not-configured",
"test": VerifyIPv4RouteNextHops,
"eos_data": [
{"vrfs": {"default": {"routes": {}}, "MGMT": {"routes": {}}}},
],
(VerifyIPv4RouteNextHops, "failure-not-configured"): {
"eos_data": [{"vrfs": {"default": {"routes": {}}, "MGMT": {"routes": {}}}}],
"inputs": {
"route_entries": [
{"prefix": "10.10.0.1/32", "vrf": "default", "strict": True, "nexthops": ["10.100.0.8", "10.100.0.10"]},
@ -438,32 +391,30 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Prefix: 10.10.0.1/32 VRF: default - prefix not found", "Prefix: 10.100.0.128/31 VRF: MGMT - prefix not found"],
},
},
{
"name": "failure-strict-failed",
"test": VerifyIPv4RouteNextHops,
(VerifyIPv4RouteNextHops, "failure-strict-failed"): {
"eos_data": [
{
"vrfs": {
"default": {
"routes": {
"10.10.0.1/32": {
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}],
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}]
}
}
},
"MGMT": {
"routes": {
"10.100.0.128/31": {
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.11", "interface": "Ethernet2"}],
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.11", "interface": "Ethernet2"}]
}
}
},
}
},
}
],
"inputs": {
"route_entries": [
@ -472,36 +423,34 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Prefix: 10.10.0.1/32 VRF: default - List of next-hops not matching - Expected: 10.100.0.10, 10.100.0.11, 10.100.0.8 "
"Actual: 10.100.0.10, 10.100.0.8",
"Prefix: 10.10.0.1/32 VRF: default - List of next-hops not matching - "
"Expected: 10.100.0.10, 10.100.0.11, 10.100.0.8 Actual: 10.100.0.10, 10.100.0.8",
"Prefix: 10.100.0.128/31 VRF: MGMT - List of next-hops not matching - Expected: 10.100.0.10, 10.100.0.8 Actual: 10.100.0.11, 10.100.0.8",
],
},
},
{
"name": "failure",
"test": VerifyIPv4RouteNextHops,
(VerifyIPv4RouteNextHops, "failure"): {
"eos_data": [
{
"vrfs": {
"default": {
"routes": {
"10.10.0.1/32": {
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}],
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}]
}
}
},
"MGMT": {
"routes": {
"10.100.0.128/31": {
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}],
"vias": [{"nexthopAddr": "10.100.0.8", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.10", "interface": "Ethernet2"}]
}
}
},
}
},
}
],
"inputs": {
"route_entries": [
@ -510,14 +459,93 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Prefix: 10.10.0.1/32 VRF: default Nexthop: 10.100.0.11 - Route not found",
"Prefix: 10.100.0.128/31 VRF: MGMT Nexthop: 10.100.0.11 - Route not found",
],
},
},
]
(VerifyRoutingStatus, "success-routing-enablement"): {
"eos_data": [
{
"v4RoutingEnabled": True,
"v6RoutingEnabled": True,
"vrrpIntfs": 0,
"v6IntfForwarding": True,
"multicastRouting": {"ipMulticastEnabled": False, "ip6MulticastEnabled": False},
"v6EcmpInfo": {"v6EcmpRouteSupport": True},
}
],
"inputs": {"ipv4_unicast": True, "ipv6_unicast": True, "ipv6_interfaces": True},
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyRoutingStatus, "success-routing-disable-all"): {
"eos_data": [
{
"v4RoutingEnabled": False,
"v6RoutingEnabled": False,
"vrrpIntfs": 0,
"multicastRouting": {"ipMulticastEnabled": False, "ip6MulticastEnabled": False},
"v6EcmpInfo": {"v6EcmpRouteSupport": False},
}
],
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyRoutingStatus, "failure-ip-multicastrouting-enablement"): {
"eos_data": [
{
"v4RoutingEnabled": False,
"v6RoutingEnabled": False,
"vrrpIntfs": 0,
"multicastRouting": {"ipMulticastEnabled": False, "ip6MulticastEnabled": False},
"v6EcmpInfo": {"v6EcmpRouteSupport": True},
}
],
"inputs": {"ipv4_multicast": True, "ipv6_multicast": True},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": [
"IPv4 multicast routing enabled status mismatch - Expected: True Actual: False",
"IPv6 multicast routing enabled status mismatch - Expected: True Actual: False",
],
},
},
(VerifyRoutingStatus, "failure-ip-routing-enablement"): {
"eos_data": [
{
"v4RoutingEnabled": False,
"v6RoutingEnabled": False,
"vrrpIntfs": 0,
"multicastRouting": {"ipMulticastEnabled": True, "ip6MulticastEnabled": True},
"v6EcmpInfo": {"v6EcmpRouteSupport": True},
}
],
"inputs": {"ipv4_unicast": True, "ipv6_unicast": True},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": [
"IPv4 unicast routing enabled status mismatch - Expected: True Actual: False",
"IPv6 unicast routing enabled status mismatch - Expected: True Actual: False",
"IPv4 multicast routing enabled status mismatch - Expected: False Actual: True",
"IPv6 multicast routing enabled status mismatch - Expected: False Actual: True",
],
},
},
(VerifyRoutingStatus, "failure-ipv6-interface-routing-enablement"): {
"eos_data": [
{
"v4RoutingEnabled": True,
"v6RoutingEnabled": True,
"vrrpIntfs": 0,
"multicastRouting": {"ipMulticastEnabled": False, "ip6MulticastEnabled": False},
"v6EcmpInfo": {"v6EcmpRouteSupport": True},
}
],
"inputs": {"ipv4_unicast": True, "ipv6_unicast": True, "ipv6_interfaces": True},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["IPv6 interfaces routing enabled status mismatch - Expected: True Actual: False"]},
},
}
class TestVerifyRoutingTableSizeInputs:

File diff suppressed because it is too large Load diff

View file

@ -5,15 +5,24 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.routing.ospf import VerifyOSPFMaxLSA, VerifyOSPFNeighborCount, VerifyOSPFNeighborState
from tests.units.anta_tests import test
from tests.units.anta_tests import AntaUnitTest, test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyOSPFNeighborState,
if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
TypeAlias = type
AntaUnitTestDataDict: TypeAlias = dict[tuple[type[AntaTest], str], AntaUnitTest]
DATA: AntaUnitTestDataDict = {
(VerifyOSPFNeighborState, "success"): {
"eos_data": [
{
"vrfs": {
@ -39,9 +48,9 @@ DATA: list[dict[str, Any]] = [
"inactivity": 1683298014.844345,
"interfaceAddress": "10.3.0.1",
},
],
},
},
]
}
}
},
"BLAH": {
"instList": {
@ -55,20 +64,17 @@ DATA: list[dict[str, Any]] = [
"adjacencyState": "full",
"inactivity": 1683298014.844345,
"interfaceAddress": "10.3.0.1",
},
],
},
},
}
]
}
}
},
},
},
}
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyOSPFNeighborState,
(VerifyOSPFNeighborState, "failure"): {
"eos_data": [
{
"vrfs": {
@ -94,9 +100,9 @@ DATA: list[dict[str, Any]] = [
"inactivity": 1683298014.844345,
"interfaceAddress": "10.3.0.1",
},
],
},
},
]
}
}
},
"BLAH": {
"instList": {
@ -110,63 +116,31 @@ DATA: list[dict[str, Any]] = [
"adjacencyState": "down",
"inactivity": 1683298014.844345,
"interfaceAddress": "10.3.0.1",
},
],
},
},
}
]
}
}
},
},
},
}
}
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Instance: 666 VRF: default Interface: 7.7.7.7 - Incorrect adjacency state - Expected: Full Actual: 2-way",
"Instance: 777 VRF: BLAH Interface: 8.8.8.8 - Incorrect adjacency state - Expected: Full Actual: down",
],
},
},
{
"name": "skipped-ospf-not-configured",
"test": VerifyOSPFNeighborState,
"eos_data": [
{
"vrfs": {},
},
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["OSPF not configured"]},
(VerifyOSPFNeighborState, "skipped-ospf-not-configured"): {
"eos_data": [{"vrfs": {}}],
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["OSPF not configured"]},
},
{
"name": "skipped-neighbor-not-found",
"test": VerifyOSPFNeighborState,
"eos_data": [
{
"vrfs": {
"default": {
"instList": {
"666": {
"ospfNeighborEntries": [],
},
},
},
"BLAH": {
"instList": {
"777": {
"ospfNeighborEntries": [],
},
},
},
},
},
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["No OSPF neighbor detected"]},
(VerifyOSPFNeighborState, "skipped-neighbor-not-found"): {
"eos_data": [{"vrfs": {"default": {"instList": {"666": {"ospfNeighborEntries": []}}}, "BLAH": {"instList": {"777": {"ospfNeighborEntries": []}}}}}],
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["No OSPF neighbor detected"]},
},
{
"name": "success",
"test": VerifyOSPFNeighborCount,
(VerifyOSPFNeighborCount, "success"): {
"eos_data": [
{
"vrfs": {
@ -192,9 +166,9 @@ DATA: list[dict[str, Any]] = [
"inactivity": 1683298014.844345,
"interfaceAddress": "10.3.0.1",
},
],
},
},
]
}
}
},
"BLAH": {
"instList": {
@ -208,20 +182,18 @@ DATA: list[dict[str, Any]] = [
"adjacencyState": "full",
"inactivity": 1683298014.844345,
"interfaceAddress": "10.3.0.1",
},
],
},
},
}
]
}
}
},
},
},
}
}
],
"inputs": {"number": 3},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-good-number-wrong-state",
"test": VerifyOSPFNeighborCount,
(VerifyOSPFNeighborCount, "failure-good-number-wrong-state"): {
"eos_data": [
{
"vrfs": {
@ -247,9 +219,9 @@ DATA: list[dict[str, Any]] = [
"inactivity": 1683298014.844345,
"interfaceAddress": "10.3.0.1",
},
],
},
},
]
}
}
},
"BLAH": {
"instList": {
@ -263,65 +235,28 @@ DATA: list[dict[str, Any]] = [
"adjacencyState": "down",
"inactivity": 1683298014.844345,
"interfaceAddress": "10.3.0.1",
},
],
},
},
}
]
}
}
},
},
},
}
}
],
"inputs": {"number": 3},
"expected": {
"result": "failure",
"messages": ["Neighbor count mismatch - Expected: 3 Actual: 1"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Neighbor count mismatch - Expected: 3 Actual: 1"]},
},
{
"name": "skipped-ospf-not-configured",
"test": VerifyOSPFNeighborCount,
"eos_data": [
{
"vrfs": {},
},
],
(VerifyOSPFNeighborCount, "skipped-ospf-not-configured"): {
"eos_data": [{"vrfs": {}}],
"inputs": {"number": 3},
"expected": {"result": "skipped", "messages": ["OSPF not configured"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["OSPF not configured"]},
},
{
"name": "skipped-no-neighbor-detected",
"test": VerifyOSPFNeighborCount,
"eos_data": [
{
"vrfs": {
"default": {
"instList": {
"666": {
"ospfNeighborEntries": [],
},
},
},
"BLAH": {
"instList": {
"777": {
"ospfNeighborEntries": [],
},
},
},
},
},
],
(VerifyOSPFNeighborCount, "skipped-no-neighbor-detected"): {
"eos_data": [{"vrfs": {"default": {"instList": {"666": {"ospfNeighborEntries": []}}}, "BLAH": {"instList": {"777": {"ospfNeighborEntries": []}}}}}],
"inputs": {"number": 3},
"expected": {
"result": "skipped",
"messages": [
"No OSPF neighbor detected",
],
},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["No OSPF neighbor detected"]},
},
{
"name": "success",
"test": VerifyOSPFMaxLSA,
(VerifyOSPFMaxLSA, "success"): {
"eos_data": [
{
"vrfs": {
@ -329,10 +264,7 @@ DATA: list[dict[str, Any]] = [
"instList": {
"1": {
"instanceId": 1,
"maxLsaInformation": {
"maxLsa": 12000,
"maxLsaThreshold": 75,
},
"maxLsaInformation": {"maxLsa": 12000, "maxLsaThreshold": 75},
"routerId": "1.1.1.1",
"lsaInformation": {
"lsaArrivalInterval": 1000,
@ -341,17 +273,14 @@ DATA: list[dict[str, Any]] = [
"lsaMaxWaitInterval": 5000,
"numLsa": 9,
},
},
},
}
}
},
"TEST": {
"instList": {
"10": {
"instanceId": 10,
"maxLsaInformation": {
"maxLsa": 1000,
"maxLsaThreshold": 75,
},
"maxLsaInformation": {"maxLsa": 1000, "maxLsaThreshold": 75},
"routerId": "20.20.20.20",
"lsaInformation": {
"lsaArrivalInterval": 1000,
@ -360,18 +289,15 @@ DATA: list[dict[str, Any]] = [
"lsaMaxWaitInterval": 5000,
"numLsa": 5,
},
},
},
}
}
},
},
},
}
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyOSPFMaxLSA,
(VerifyOSPFMaxLSA, "failure"): {
"eos_data": [
{
"vrfs": {
@ -379,10 +305,7 @@ DATA: list[dict[str, Any]] = [
"instList": {
"1": {
"instanceId": 1,
"maxLsaInformation": {
"maxLsa": 12000,
"maxLsaThreshold": 75,
},
"maxLsaInformation": {"maxLsa": 12000, "maxLsaThreshold": 75},
"routerId": "1.1.1.1",
"lsaInformation": {
"lsaArrivalInterval": 1000,
@ -391,17 +314,14 @@ DATA: list[dict[str, Any]] = [
"lsaMaxWaitInterval": 5000,
"numLsa": 11500,
},
},
},
}
}
},
"TEST": {
"instList": {
"10": {
"instanceId": 10,
"maxLsaInformation": {
"maxLsa": 1000,
"maxLsaThreshold": 75,
},
"maxLsaInformation": {"maxLsa": 1000, "maxLsaThreshold": 75},
"routerId": "20.20.20.20",
"lsaInformation": {
"lsaArrivalInterval": 1000,
@ -410,30 +330,19 @@ DATA: list[dict[str, Any]] = [
"lsaMaxWaitInterval": 5000,
"numLsa": 1500,
},
},
},
}
}
},
},
},
}
}
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Instance: 1 - Crossed the maximum LSA threshold - Expected: < 9000 Actual: 11500",
"Instance: 10 - Crossed the maximum LSA threshold - Expected: < 750 Actual: 1500",
],
},
},
{
"name": "skipped",
"test": VerifyOSPFMaxLSA,
"eos_data": [
{
"vrfs": {},
},
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["OSPF not configured"]},
},
]
(VerifyOSPFMaxLSA, "skipped"): {"eos_data": [{"vrfs": {}}], "expected": {"result": AntaTestStatus.SKIPPED, "messages": ["OSPF not configured"]}},
}

View file

@ -5,8 +5,11 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.aaa import (
VerifyAcctConsoleMethods,
VerifyAcctDefaultMethods,
@ -18,510 +21,326 @@ from anta.tests.aaa import (
)
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyTacacsSourceIntf,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyTacacsSourceIntf, "success"): {
"eos_data": [
{
"tacacsServers": [
{
"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"},
},
],
"tacacsServers": [{"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"}}],
"groups": {"GROUP1": {"serverGroup": "TACACS+", "members": [{"hostname": "SERVER1", "authport": 49, "vrf": "MGMT"}]}},
"srcIntf": {"MGMT": "Management0"},
},
}
],
"inputs": {"intf": "Management0", "vrf": "MGMT"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-not-configured",
"test": VerifyTacacsSourceIntf,
"eos_data": [
{
"tacacsServers": [],
"groups": {},
"srcIntf": {},
},
],
(VerifyTacacsSourceIntf, "failure-not-configured"): {
"eos_data": [{"tacacsServers": [], "groups": {}, "srcIntf": {}}],
"inputs": {"intf": "Management0", "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["VRF: MGMT Source Interface: Management0 - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: MGMT Source Interface: Management0 - Not configured"]},
},
{
"name": "failure-wrong-intf",
"test": VerifyTacacsSourceIntf,
(VerifyTacacsSourceIntf, "failure-wrong-intf"): {
"eos_data": [
{
"tacacsServers": [
{
"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"},
},
],
"tacacsServers": [{"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"}}],
"groups": {"GROUP1": {"serverGroup": "TACACS+", "members": [{"hostname": "SERVER1", "authport": 49, "vrf": "MGMT"}]}},
"srcIntf": {"MGMT": "Management1"},
},
}
],
"inputs": {"intf": "Management0", "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["VRF: MGMT - Source interface mismatch - Expected: Management0 Actual: Management1"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: MGMT - Source interface mismatch - Expected: Management0 Actual: Management1"]},
},
{
"name": "failure-wrong-vrf",
"test": VerifyTacacsSourceIntf,
(VerifyTacacsSourceIntf, "failure-wrong-vrf"): {
"eos_data": [
{
"tacacsServers": [
{
"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"},
},
],
"tacacsServers": [{"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"}}],
"groups": {"GROUP1": {"serverGroup": "TACACS+", "members": [{"hostname": "SERVER1", "authport": 49, "vrf": "MGMT"}]}},
"srcIntf": {"PROD": "Management0"},
},
}
],
"inputs": {"intf": "Management0", "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["VRF: MGMT Source Interface: Management0 - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: MGMT Source Interface: Management0 - Not configured"]},
},
{
"name": "success",
"test": VerifyTacacsServers,
(VerifyTacacsServers, "success"): {
"eos_data": [
{
"tacacsServers": [
{
"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"},
},
],
"tacacsServers": [{"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"}}],
"groups": {"GROUP1": {"serverGroup": "TACACS+", "members": [{"hostname": "SERVER1", "authport": 49, "vrf": "MGMT"}]}},
"srcIntf": {"MGMT": "Management0"},
},
}
],
"inputs": {"servers": ["10.22.10.91"], "vrf": "MGMT"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-servers",
"test": VerifyTacacsServers,
"eos_data": [
{
"tacacsServers": [],
"groups": {},
"srcIntf": {},
},
],
(VerifyTacacsServers, "failure-no-servers"): {
"eos_data": [{"tacacsServers": [], "groups": {}, "srcIntf": {}}],
"inputs": {"servers": ["10.22.10.91"], "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["No TACACS servers are configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No TACACS servers are configured"]},
},
{
"name": "failure-not-configured",
"test": VerifyTacacsServers,
(VerifyTacacsServers, "failure-not-configured"): {
"eos_data": [
{
"tacacsServers": [
{
"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"},
},
],
"tacacsServers": [{"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"}}],
"groups": {"GROUP1": {"serverGroup": "TACACS+", "members": [{"hostname": "SERVER1", "authport": 49, "vrf": "MGMT"}]}},
"srcIntf": {"MGMT": "Management0"},
},
}
],
"inputs": {"servers": ["10.22.10.91", "10.22.10.92"], "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["TACACS servers 10.22.10.92 are not configured in VRF MGMT"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["TACACS servers 10.22.10.92 are not configured in VRF MGMT"]},
},
{
"name": "failure-wrong-vrf",
"test": VerifyTacacsServers,
(VerifyTacacsServers, "failure-wrong-vrf"): {
"eos_data": [
{
"tacacsServers": [
{
"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "PROD"},
},
],
"tacacsServers": [{"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "PROD"}}],
"groups": {"GROUP1": {"serverGroup": "TACACS+", "members": [{"hostname": "SERVER1", "authport": 49, "vrf": "MGMT"}]}},
"srcIntf": {"MGMT": "Management0"},
},
}
],
"inputs": {"servers": ["10.22.10.91"], "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["TACACS servers 10.22.10.91 are not configured in VRF MGMT"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["TACACS servers 10.22.10.91 are not configured in VRF MGMT"]},
},
{
"name": "success",
"test": VerifyTacacsServerGroups,
(VerifyTacacsServerGroups, "success"): {
"eos_data": [
{
"tacacsServers": [
{
"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"},
},
],
"tacacsServers": [{"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"}}],
"groups": {"GROUP1": {"serverGroup": "TACACS+", "members": [{"hostname": "SERVER1", "authport": 49, "vrf": "MGMT"}]}},
"srcIntf": {"MGMT": "Management0"},
},
}
],
"inputs": {"groups": ["GROUP1"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-server-groups",
"test": VerifyTacacsServerGroups,
"eos_data": [
{
"tacacsServers": [],
"groups": {},
"srcIntf": {},
},
],
(VerifyTacacsServerGroups, "failure-no-server-groups"): {
"eos_data": [{"tacacsServers": [], "groups": {}, "srcIntf": {}}],
"inputs": {"groups": ["GROUP1"]},
"expected": {"result": "failure", "messages": ["No TACACS server group(s) are configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No TACACS server group(s) are configured"]},
},
{
"name": "failure-not-configured",
"test": VerifyTacacsServerGroups,
(VerifyTacacsServerGroups, "failure-not-configured"): {
"eos_data": [
{
"tacacsServers": [
{
"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"},
},
],
"tacacsServers": [{"serverInfo": {"hostname": "10.22.10.91", "authport": 49, "vrf": "MGMT"}}],
"groups": {"GROUP2": {"serverGroup": "TACACS+", "members": [{"hostname": "SERVER1", "authport": 49, "vrf": "MGMT"}]}},
"srcIntf": {"MGMT": "Management0"},
},
}
],
"inputs": {"groups": ["GROUP1"]},
"expected": {"result": "failure", "messages": ["TACACS server group(s) GROUP1 are not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["TACACS server group(s) GROUP1 are not configured"]},
},
{
"name": "success-login-enable",
"test": VerifyAuthenMethods,
(VerifyAuthenMethods, "success-login-enable"): {
"eos_data": [
{
"loginAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}, "login": {"methods": ["group tacacs+", "local"]}},
"enableAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}},
"dot1xAuthenMethods": {"default": {"methods": ["group radius"]}},
},
}
],
"inputs": {"methods": ["tacacs+", "local"], "types": ["login", "enable"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-dot1x",
"test": VerifyAuthenMethods,
(VerifyAuthenMethods, "success-dot1x"): {
"eos_data": [
{
"loginAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}, "login": {"methods": ["group tacacs+", "local"]}},
"enableAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}},
"dot1xAuthenMethods": {"default": {"methods": ["group radius"]}},
},
}
],
"inputs": {"methods": ["radius"], "types": ["dot1x"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-login-console",
"test": VerifyAuthenMethods,
(VerifyAuthenMethods, "failure-no-login-console"): {
"eos_data": [
{
"loginAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}},
"enableAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}},
"dot1xAuthenMethods": {"default": {"methods": ["group radius"]}},
},
}
],
"inputs": {"methods": ["tacacs+", "local"], "types": ["login", "enable"]},
"expected": {"result": "failure", "messages": ["AAA authentication methods are not configured for login console"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA authentication methods are not configured for login console"]},
},
{
"name": "failure-login-console",
"test": VerifyAuthenMethods,
(VerifyAuthenMethods, "failure-login-console"): {
"eos_data": [
{
"loginAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}, "login": {"methods": ["group radius", "local"]}},
"enableAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}},
"dot1xAuthenMethods": {"default": {"methods": ["group radius"]}},
},
}
],
"inputs": {"methods": ["tacacs+", "local"], "types": ["login", "enable"]},
"expected": {"result": "failure", "messages": ["AAA authentication methods group tacacs+, local are not matching for login console"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA authentication methods group tacacs+, local are not matching for login console"]},
},
{
"name": "failure-login-default",
"test": VerifyAuthenMethods,
(VerifyAuthenMethods, "failure-login-default"): {
"eos_data": [
{
"loginAuthenMethods": {"default": {"methods": ["group radius", "local"]}, "login": {"methods": ["group tacacs+", "local"]}},
"enableAuthenMethods": {"default": {"methods": ["group tacacs+", "local"]}},
"dot1xAuthenMethods": {"default": {"methods": ["group radius"]}},
},
}
],
"inputs": {"methods": ["tacacs+", "local"], "types": ["login", "enable"]},
"expected": {"result": "failure", "messages": ["AAA authentication methods group tacacs+, local are not matching for login"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA authentication methods group tacacs+, local are not matching for login"]},
},
{
"name": "success",
"test": VerifyAuthzMethods,
(VerifyAuthzMethods, "success"): {
"eos_data": [
{
"commandsAuthzMethods": {"privilege0-15": {"methods": ["group tacacs+", "local"]}},
"execAuthzMethods": {"exec": {"methods": ["group tacacs+", "local"]}},
},
}
],
"inputs": {"methods": ["tacacs+", "local"], "types": ["commands", "exec"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-skipping-exec",
"test": VerifyAuthzMethods,
(VerifyAuthzMethods, "success-skipping-exec"): {
"eos_data": [
{
"commandsAuthzMethods": {"privilege0-15": {"methods": ["group tacacs+", "local"]}},
"execAuthzMethods": {"exec": {"methods": ["group tacacs+", "local"]}},
},
}
],
"inputs": {"methods": ["tacacs+", "local"], "types": ["commands"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-commands",
"test": VerifyAuthzMethods,
(VerifyAuthzMethods, "failure-commands"): {
"eos_data": [
{
"commandsAuthzMethods": {"privilege0-15": {"methods": ["group radius", "local"]}},
"execAuthzMethods": {"exec": {"methods": ["group tacacs+", "local"]}},
},
}
],
"inputs": {"methods": ["tacacs+", "local"], "types": ["commands", "exec"]},
"expected": {"result": "failure", "messages": ["AAA authorization methods group tacacs+, local are not matching for commands"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA authorization methods group tacacs+, local are not matching for commands"]},
},
{
"name": "failure-exec",
"test": VerifyAuthzMethods,
(VerifyAuthzMethods, "failure-exec"): {
"eos_data": [
{
"commandsAuthzMethods": {"privilege0-15": {"methods": ["group tacacs+", "local"]}},
"execAuthzMethods": {"exec": {"methods": ["group radius", "local"]}},
},
}
],
"inputs": {"methods": ["tacacs+", "local"], "types": ["commands", "exec"]},
"expected": {"result": "failure", "messages": ["AAA authorization methods group tacacs+, local are not matching for exec"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA authorization methods group tacacs+, local are not matching for exec"]},
},
{
"name": "success-commands-exec-system",
"test": VerifyAcctDefaultMethods,
(VerifyAcctDefaultMethods, "success-commands-exec-system"): {
"eos_data": [
{
"commandsAcctMethods": {"privilege0-15": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"execAcctMethods": {"exec": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"systemAcctMethods": {"system": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["commands", "exec", "system"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-dot1x",
"test": VerifyAcctDefaultMethods,
(VerifyAcctDefaultMethods, "success-dot1x"): {
"eos_data": [
{
"commandsAcctMethods": {"privilege0-15": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"execAcctMethods": {"exec": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"systemAcctMethods": {"system": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"dot1xAcctMethods": {"dot1x": {"defaultAction": "startStop", "defaultMethods": ["group radius", "logging"], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["radius", "logging"], "types": ["dot1x"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-not-configured",
"test": VerifyAcctDefaultMethods,
(VerifyAcctDefaultMethods, "failure-not-configured"): {
"eos_data": [
{
"commandsAcctMethods": {"privilege0-15": {"defaultMethods": [], "consoleMethods": []}},
"execAcctMethods": {"exec": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"systemAcctMethods": {"system": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["commands", "exec", "system"]},
"expected": {"result": "failure", "messages": ["AAA default accounting is not configured for commands"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA default accounting is not configured for commands"]},
},
{
"name": "failure-not-configured-empty",
"test": VerifyAcctDefaultMethods,
(VerifyAcctDefaultMethods, "failure-not-configured-empty"): {
"eos_data": [
{
"systemAcctMethods": {"system": {"defaultMethods": [], "consoleMethods": []}},
"execAcctMethods": {"exec": {"defaultMethods": [], "consoleMethods": []}},
"commandsAcctMethods": {"privilege0-15": {"defaultMethods": [], "consoleMethods": []}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["commands", "exec", "system"]},
"expected": {"result": "failure", "messages": ["AAA default accounting is not configured for system, exec, commands"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA default accounting is not configured for system, exec, commands"]},
},
{
"name": "failure-not-matching",
"test": VerifyAcctDefaultMethods,
(VerifyAcctDefaultMethods, "failure-not-matching"): {
"eos_data": [
{
"commandsAcctMethods": {"privilege0-15": {"defaultAction": "startStop", "defaultMethods": ["group radius", "logging"], "consoleMethods": []}},
"execAcctMethods": {"exec": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"systemAcctMethods": {"system": {"defaultAction": "startStop", "defaultMethods": ["group tacacs+", "logging"], "consoleMethods": []}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["commands", "exec", "system"]},
"expected": {"result": "failure", "messages": ["AAA accounting default methods group tacacs+, logging are not matching for commands"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA accounting default methods group tacacs+, logging are not matching for commands"]},
},
{
"name": "success-commands-exec-system",
"test": VerifyAcctConsoleMethods,
(VerifyAcctConsoleMethods, "success-commands-exec-system"): {
"eos_data": [
{
"commandsAcctMethods": {
"privilege0-15": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"execAcctMethods": {
"exec": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"systemAcctMethods": {
"system": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"commandsAcctMethods": {"privilege0-15": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"execAcctMethods": {"exec": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"systemAcctMethods": {"system": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["commands", "exec", "system"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-dot1x",
"test": VerifyAcctConsoleMethods,
(VerifyAcctConsoleMethods, "success-dot1x"): {
"eos_data": [
{
"commandsAcctMethods": {
"privilege0-15": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"execAcctMethods": {
"exec": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"systemAcctMethods": {
"system": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"dot1xAcctMethods": {
"dot1x": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
},
"commandsAcctMethods": {"privilege0-15": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"execAcctMethods": {"exec": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"systemAcctMethods": {"system": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["dot1x"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-not-configured",
"test": VerifyAcctConsoleMethods,
(VerifyAcctConsoleMethods, "failure-not-configured"): {
"eos_data": [
{
"commandsAcctMethods": {
"privilege0-15": {
"defaultMethods": [],
"consoleMethods": [],
},
},
"execAcctMethods": {
"exec": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"systemAcctMethods": {
"system": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"commandsAcctMethods": {"privilege0-15": {"defaultMethods": [], "consoleMethods": []}},
"execAcctMethods": {"exec": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"systemAcctMethods": {"system": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["commands", "exec", "system"]},
"expected": {"result": "failure", "messages": ["AAA console accounting is not configured for commands"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA console accounting is not configured for commands"]},
},
{
"name": "failure-not-configured-empty",
"test": VerifyAcctConsoleMethods,
(VerifyAcctConsoleMethods, "failure-not-configured-empty"): {
"eos_data": [
{
"systemAcctMethods": {"system": {"defaultMethods": [], "consoleMethods": []}},
"execAcctMethods": {"exec": {"defaultMethods": [], "consoleMethods": []}},
"commandsAcctMethods": {"privilege0-15": {"defaultMethods": [], "consoleMethods": []}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["commands", "exec", "system"]},
"expected": {"result": "failure", "messages": ["AAA console accounting is not configured for system, exec, commands"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA console accounting is not configured for system, exec, commands"]},
},
{
"name": "failure-not-matching",
"test": VerifyAcctConsoleMethods,
(VerifyAcctConsoleMethods, "failure-not-matching"): {
"eos_data": [
{
"commandsAcctMethods": {
"privilege0-15": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group radius", "logging"],
},
},
"execAcctMethods": {
"exec": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"systemAcctMethods": {
"system": {
"defaultMethods": [],
"consoleAction": "startStop",
"consoleMethods": ["group tacacs+", "logging"],
},
},
"commandsAcctMethods": {"privilege0-15": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group radius", "logging"]}},
"execAcctMethods": {"exec": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"systemAcctMethods": {"system": {"defaultMethods": [], "consoleAction": "startStop", "consoleMethods": ["group tacacs+", "logging"]}},
"dot1xAcctMethods": {"dot1x": {"defaultMethods": [], "consoleMethods": []}},
},
}
],
"inputs": {"methods": ["tacacs+", "logging"], "types": ["commands", "exec", "system"]},
"expected": {"result": "failure", "messages": ["AAA accounting console methods group tacacs+, logging are not matching for commands"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA accounting console methods group tacacs+, logging are not matching for commands"]},
},
]
}

View file

@ -5,15 +5,19 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.avt import VerifyAVTPathHealth, VerifyAVTRole, VerifyAVTSpecificPath
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyAVTPathHealth,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyAVTPathHealth, "success"): {
"eos_data": [
{
"vrfs": {
@ -21,18 +25,10 @@ DATA: list[dict[str, Any]] = [
"avts": {
"DATA-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:9": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:1": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:9": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:1": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
}
}
@ -41,12 +37,8 @@ DATA: list[dict[str, Any]] = [
"avts": {
"GUEST-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
}
}
@ -55,28 +47,16 @@ DATA: list[dict[str, Any]] = [
"avts": {
"CONTROL-PLANE-PROFILE": {
"avtPaths": {
"direct:9": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:1": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:9": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:1": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
},
"DEFAULT-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
},
}
@ -85,21 +65,14 @@ DATA: list[dict[str, Any]] = [
}
],
"inputs": {},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-avt-not-configured",
"test": VerifyAVTPathHealth,
(VerifyAVTPathHealth, "failure-avt-not-configured"): {
"eos_data": [{"vrfs": {}}],
"inputs": {},
"expected": {
"result": "failure",
"messages": ["Adaptive virtual topology paths are not configured"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Adaptive virtual topology paths are not configured"]},
},
{
"name": "failure-not-active-path",
"test": VerifyAVTPathHealth,
(VerifyAVTPathHealth, "failure-not-active-path"): {
"eos_data": [
{
"vrfs": {
@ -107,18 +80,10 @@ DATA: list[dict[str, Any]] = [
"avts": {
"DATA-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:9": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:1": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:9": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:1": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
}
}
@ -127,12 +92,8 @@ DATA: list[dict[str, Any]] = [
"avts": {
"GUEST-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": False},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": False}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
}
}
@ -141,28 +102,16 @@ DATA: list[dict[str, Any]] = [
"avts": {
"CONTROL-PLANE-PROFILE": {
"avtPaths": {
"direct:9": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:1": {
"flags": {"directPath": True, "valid": True, "active": False},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:9": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:1": {"flags": {"directPath": True, "valid": True, "active": False}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
},
"DEFAULT-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": False},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": False}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
},
}
@ -172,7 +121,7 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"VRF: guest Profile: GUEST-AVT-POLICY-DEFAULT AVT path: direct:10 - Not active",
"VRF: default Profile: CONTROL-PLANE-PROFILE AVT path: direct:1 - Not active",
@ -180,9 +129,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-invalid-path",
"test": VerifyAVTPathHealth,
(VerifyAVTPathHealth, "failure-invalid-path"): {
"eos_data": [
{
"vrfs": {
@ -190,18 +137,10 @@ DATA: list[dict[str, Any]] = [
"avts": {
"DATA-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:9": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {
"flags": {"directPath": True, "valid": False, "active": True},
},
"direct:1": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:9": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:10": {"flags": {"directPath": True, "valid": False, "active": True}},
"direct:1": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
}
}
@ -210,12 +149,8 @@ DATA: list[dict[str, Any]] = [
"avts": {
"GUEST-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": False, "active": True},
},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": False, "active": True}},
}
}
}
@ -224,28 +159,16 @@ DATA: list[dict[str, Any]] = [
"avts": {
"CONTROL-PLANE-PROFILE": {
"avtPaths": {
"direct:9": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {
"flags": {"directPath": True, "valid": False, "active": True},
},
"direct:1": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:9": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:10": {"flags": {"directPath": True, "valid": False, "active": True}},
"direct:1": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
},
"DEFAULT-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": False, "active": True},
},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": False, "active": True}},
}
},
}
@ -255,7 +178,7 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"VRF: data Profile: DATA-AVT-POLICY-DEFAULT AVT path: direct:10 - Invalid",
"VRF: guest Profile: GUEST-AVT-POLICY-DEFAULT AVT path: direct:8 - Invalid",
@ -264,9 +187,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-not-active-and-invalid",
"test": VerifyAVTPathHealth,
(VerifyAVTPathHealth, "failure-not-active-and-invalid"): {
"eos_data": [
{
"vrfs": {
@ -274,18 +195,10 @@ DATA: list[dict[str, Any]] = [
"avts": {
"DATA-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:9": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {
"flags": {"directPath": True, "valid": False, "active": False},
},
"direct:1": {
"flags": {"directPath": True, "valid": True, "active": False},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:9": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:10": {"flags": {"directPath": True, "valid": False, "active": False}},
"direct:1": {"flags": {"directPath": True, "valid": True, "active": False}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
}
}
@ -294,12 +207,8 @@ DATA: list[dict[str, Any]] = [
"avts": {
"GUEST-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:10": {
"flags": {"directPath": True, "valid": False, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": False, "active": False},
},
"direct:10": {"flags": {"directPath": True, "valid": False, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": False, "active": False}},
}
}
}
@ -308,28 +217,16 @@ DATA: list[dict[str, Any]] = [
"avts": {
"CONTROL-PLANE-PROFILE": {
"avtPaths": {
"direct:9": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:10": {
"flags": {"directPath": True, "valid": False, "active": False},
},
"direct:1": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:8": {
"flags": {"directPath": True, "valid": True, "active": True},
},
"direct:9": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:10": {"flags": {"directPath": True, "valid": False, "active": False}},
"direct:1": {"flags": {"directPath": True, "valid": True, "active": True}},
"direct:8": {"flags": {"directPath": True, "valid": True, "active": True}},
}
},
"DEFAULT-AVT-POLICY-DEFAULT": {
"avtPaths": {
"direct:10": {
"flags": {"directPath": True, "valid": True, "active": False},
},
"direct:8": {
"flags": {"directPath": True, "valid": False, "active": False},
},
"direct:10": {"flags": {"directPath": True, "valid": True, "active": False}},
"direct:8": {"flags": {"directPath": True, "valid": False, "active": False}},
}
},
}
@ -339,7 +236,7 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"VRF: data Profile: DATA-AVT-POLICY-DEFAULT AVT path: direct:10 - Invalid and not active",
"VRF: data Profile: DATA-AVT-POLICY-DEFAULT AVT path: direct:1 - Not active",
@ -351,9 +248,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyAVTSpecificPath,
(VerifyAVTSpecificPath, "success"): {
"eos_data": [
{
"vrfs": {
@ -383,7 +278,7 @@ DATA: list[dict[str, Any]] = [
},
}
}
},
}
},
"data": {
"avts": {
@ -415,11 +310,11 @@ DATA: list[dict[str, Any]] = [
"destination": "10.101.255.1",
},
}
},
}
}
},
}
},
}
],
"inputs": {
"avt_paths": [
@ -428,14 +323,10 @@ DATA: list[dict[str, Any]] = [
{"avt_name": "DATA-AVT-POLICY-CONTROL-PLANE", "vrf": "data", "destination": "10.101.255.1", "next_hop": "10.101.255.2"},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-peer",
"test": VerifyAVTSpecificPath,
"eos_data": [
{"vrfs": {}},
],
(VerifyAVTSpecificPath, "failure-no-peer"): {
"eos_data": [{"vrfs": {}}],
"inputs": {
"avt_paths": [
{"avt_name": "MGMT-AVT-POLICY-DEFAULT", "vrf": "default", "destination": "10.101.255.2", "next_hop": "10.101.255.1", "path_type": "multihop"},
@ -443,13 +334,11 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["AVT: MGMT-AVT-POLICY-DEFAULT VRF: default Destination: 10.101.255.2 Next-hop: 10.101.255.1 - No AVT path configured"],
},
},
{
"name": "failure-path_type_check_true",
"test": VerifyAVTSpecificPath,
(VerifyAVTSpecificPath, "failure-path_type_check_true"): {
"eos_data": [
{
"vrfs": {
@ -469,7 +358,7 @@ DATA: list[dict[str, Any]] = [
},
}
}
},
}
},
"data": {
"avts": {
@ -486,11 +375,11 @@ DATA: list[dict[str, Any]] = [
"destination": "10.101.255.3",
},
}
},
}
}
},
}
},
}
],
"inputs": {
"avt_paths": [
@ -505,16 +394,14 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"AVT: DEFAULT-AVT-POLICY-CONTROL-PLANE VRF: default Destination: 10.101.255.2 Next-hop: 10.101.255.11 Path Type: multihop - Path not found",
"AVT: DATA-AVT-POLICY-CONTROL-PLANE VRF: data Destination: 10.101.255.1 Next-hop: 10.101.255.21 Path Type: direct - Path not found",
],
},
},
{
"name": "failure-path_type_check_false",
"test": VerifyAVTSpecificPath,
(VerifyAVTSpecificPath, "failure-path_type_check_false"): {
"eos_data": [
{
"vrfs": {
@ -534,7 +421,7 @@ DATA: list[dict[str, Any]] = [
},
}
}
},
}
},
"data": {
"avts": {
@ -551,34 +438,27 @@ DATA: list[dict[str, Any]] = [
"destination": "10.101.255.3",
},
}
},
}
}
},
}
},
}
],
"inputs": {
"avt_paths": [
{
"avt_name": "DEFAULT-AVT-POLICY-CONTROL-PLANE",
"vrf": "default",
"destination": "10.101.255.2",
"next_hop": "10.101.255.11",
},
{"avt_name": "DEFAULT-AVT-POLICY-CONTROL-PLANE", "vrf": "default", "destination": "10.101.255.2", "next_hop": "10.101.255.11"},
{"avt_name": "DATA-AVT-POLICY-CONTROL-PLANE", "vrf": "data", "destination": "10.101.255.1", "next_hop": "10.101.255.21"},
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"AVT: DEFAULT-AVT-POLICY-CONTROL-PLANE VRF: default Destination: 10.101.255.2 Next-hop: 10.101.255.11 - Path not found",
"AVT: DATA-AVT-POLICY-CONTROL-PLANE VRF: data Destination: 10.101.255.1 Next-hop: 10.101.255.21 - Path not found",
],
},
},
{
"name": "failure-incorrect-path",
"test": VerifyAVTSpecificPath,
(VerifyAVTSpecificPath, "failure-incorrect-path"): {
"eos_data": [
{
"vrfs": {
@ -590,10 +470,10 @@ DATA: list[dict[str, Any]] = [
"flags": {"directPath": False, "valid": False, "active": True},
"nexthopAddr": "10.101.255.1",
"destination": "10.101.255.2",
},
}
}
}
},
}
},
"data": {
"avts": {
@ -625,11 +505,11 @@ DATA: list[dict[str, Any]] = [
"destination": "10.101.255.1",
},
}
},
}
}
},
}
},
}
],
"inputs": {
"avt_paths": [
@ -644,29 +524,21 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"AVT: DEFAULT-AVT-POLICY-CONTROL-PLANE VRF: default Destination: 10.101.255.2 Next-hop: 10.101.255.1 - "
"Incorrect path multihop:3 - Valid: False Active: True",
"AVT: DATA-AVT-POLICY-CONTROL-PLANE VRF: data Destination: 10.101.255.1 Next-hop: 10.101.255.1 - "
"Incorrect path direct:10 - Valid: False Active: True",
"AVT: DATA-AVT-POLICY-CONTROL-PLANE VRF: data Destination: 10.101.255.1 Next-hop: 10.101.255.1 - "
"Incorrect path direct:9 - Valid: True Active: False",
"AVT: DEFAULT-AVT-POLICY-CONTROL-PLANE VRF: default Destination: 10.101.255.2 Next-hop: 10.101.255.1 - Incorrect path multihop:3 - "
"Valid: False Active: True",
"AVT: DATA-AVT-POLICY-CONTROL-PLANE VRF: data Destination: 10.101.255.1 Next-hop: 10.101.255.1 - Incorrect path direct:10 - "
"Valid: False Active: True",
"AVT: DATA-AVT-POLICY-CONTROL-PLANE VRF: data Destination: 10.101.255.1 Next-hop: 10.101.255.1 - Incorrect path direct:9 - "
"Valid: True Active: False",
],
},
},
{
"name": "success",
"test": VerifyAVTRole,
"eos_data": [{"role": "edge"}],
"inputs": {"role": "edge"},
"expected": {"result": "success"},
},
{
"name": "failure-incorrect-role",
"test": VerifyAVTRole,
(VerifyAVTRole, "success"): {"eos_data": [{"role": "edge"}], "inputs": {"role": "edge"}, "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyAVTRole, "failure-incorrect-role"): {
"eos_data": [{"role": "transit"}],
"inputs": {"role": "edge"},
"expected": {"result": "failure", "messages": ["AVT role mismatch - Expected: edge Actual: transit"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AVT role mismatch - Expected: edge Actual: transit"]},
},
]
}

View file

@ -6,15 +6,19 @@
# pylint: disable=C0302
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.bfd import VerifyBFDPeersHealth, VerifyBFDPeersIntervals, VerifyBFDPeersRegProtocols, VerifyBFDSpecificPeers
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyBFDPeersIntervals,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyBFDPeersIntervals, "success"): {
"eos_data": [
{
"vrfs": {
@ -22,14 +26,7 @@ DATA: list[dict[str, Any]] = [
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 1200000,
"operRxInterval": 1200000,
"detectMult": 3,
"detectTime": 3600000,
}
}
"": {"peerStatsDetail": {"operTxInterval": 1200000, "operRxInterval": 1200000, "detectMult": 3, "detectTime": 3600000}}
}
}
}
@ -38,14 +35,7 @@ DATA: list[dict[str, Any]] = [
"ipv4Neighbors": {
"192.0.255.70": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 1200000,
"operRxInterval": 1200000,
"detectMult": 3,
"detectTime": 3600000,
}
}
"": {"peerStatsDetail": {"operTxInterval": 1200000, "operRxInterval": 1200000, "detectMult": 3, "detectTime": 3600000}}
}
}
}
@ -59,11 +49,9 @@ DATA: list[dict[str, Any]] = [
{"peer_address": "192.0.255.70", "vrf": "MGMT", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-detection-time",
"test": VerifyBFDPeersIntervals,
(VerifyBFDPeersIntervals, "success-detection-time"): {
"eos_data": [
{
"vrfs": {
@ -71,14 +59,7 @@ DATA: list[dict[str, Any]] = [
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 1200000,
"operRxInterval": 1200000,
"detectMult": 3,
"detectTime": 3600000,
}
}
"": {"peerStatsDetail": {"operTxInterval": 1200000, "operRxInterval": 1200000, "detectMult": 3, "detectTime": 3600000}}
}
}
}
@ -87,14 +68,7 @@ DATA: list[dict[str, Any]] = [
"ipv4Neighbors": {
"192.0.255.70": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 1200000,
"operRxInterval": 1200000,
"detectMult": 3,
"detectTime": 3600000,
}
}
"": {"peerStatsDetail": {"operTxInterval": 1200000, "operRxInterval": 1200000, "detectMult": 3, "detectTime": 3600000}}
}
}
}
@ -108,11 +82,9 @@ DATA: list[dict[str, Any]] = [
{"peer_address": "192.0.255.70", "vrf": "MGMT", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3, "detection_time": 3600},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-peer",
"test": VerifyBFDPeersIntervals,
(VerifyBFDPeersIntervals, "failure-no-peer"): {
"eos_data": [
{
"vrfs": {
@ -120,14 +92,7 @@ DATA: list[dict[str, Any]] = [
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 1200000,
"operRxInterval": 1200000,
"detectMult": 3,
"detectTime": 3600000,
}
}
"": {"peerStatsDetail": {"operTxInterval": 1200000, "operRxInterval": 1200000, "detectMult": 3, "detectTime": 3600000}}
}
}
}
@ -136,14 +101,7 @@ DATA: list[dict[str, Any]] = [
"ipv4Neighbors": {
"192.0.255.71": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 1200000,
"operRxInterval": 1200000,
"detectMult": 3,
"detectTime": 3600000,
}
}
"": {"peerStatsDetail": {"operTxInterval": 1200000, "operRxInterval": 1200000, "detectMult": 3, "detectTime": 3600000}}
}
}
}
@ -157,17 +115,9 @@ DATA: list[dict[str, Any]] = [
{"peer_address": "192.0.255.70", "vrf": "MGMT", "tx_interval": 1200, "rx_interval": 1200, "multiplier": 3, "detection_time": 3600},
]
},
"expected": {
"result": "failure",
"messages": [
"Peer: 192.0.255.7 VRF: CS - Not found",
"Peer: 192.0.255.70 VRF: MGMT - Not found",
],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Peer: 192.0.255.7 VRF: CS - Not found", "Peer: 192.0.255.70 VRF: MGMT - Not found"]},
},
{
"name": "failure-incorrect-timers",
"test": VerifyBFDPeersIntervals,
(VerifyBFDPeersIntervals, "failure-incorrect-timers"): {
"eos_data": [
{
"vrfs": {
@ -175,14 +125,7 @@ DATA: list[dict[str, Any]] = [
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 1300000,
"operRxInterval": 1200000,
"detectMult": 4,
"detectTime": 4000000,
}
}
"": {"peerStatsDetail": {"operTxInterval": 1300000, "operRxInterval": 1200000, "detectMult": 4, "detectTime": 4000000}}
}
}
}
@ -190,16 +133,7 @@ DATA: list[dict[str, Any]] = [
"MGMT": {
"ipv4Neighbors": {
"192.0.255.70": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 120000,
"operRxInterval": 120000,
"detectMult": 5,
"detectTime": 4000000,
}
}
}
"peerStats": {"": {"peerStatsDetail": {"operTxInterval": 120000, "operRxInterval": 120000, "detectMult": 5, "detectTime": 4000000}}}
}
}
},
@ -213,7 +147,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 192.0.255.7 VRF: default - Incorrect Transmit interval - Expected: 1200 Actual: 1300",
"Peer: 192.0.255.7 VRF: default - Incorrect Multiplier - Expected: 3 Actual: 4",
@ -223,9 +157,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-incorrect-timers-with-detection-time",
"test": VerifyBFDPeersIntervals,
(VerifyBFDPeersIntervals, "failure-incorrect-timers-with-detection-time"): {
"eos_data": [
{
"vrfs": {
@ -233,14 +165,7 @@ DATA: list[dict[str, Any]] = [
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 1300000,
"operRxInterval": 1200000,
"detectMult": 4,
"detectTime": 4000000,
}
}
"": {"peerStatsDetail": {"operTxInterval": 1300000, "operRxInterval": 1200000, "detectMult": 4, "detectTime": 4000000}}
}
}
}
@ -248,16 +173,7 @@ DATA: list[dict[str, Any]] = [
"MGMT": {
"ipv4Neighbors": {
"192.0.255.70": {
"peerStats": {
"": {
"peerStatsDetail": {
"operTxInterval": 120000,
"operRxInterval": 120000,
"detectMult": 5,
"detectTime": 4000000,
}
}
}
"peerStats": {"": {"peerStatsDetail": {"operTxInterval": 120000, "operRxInterval": 120000, "detectMult": 5, "detectTime": 4000000}}}
}
}
},
@ -271,7 +187,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 192.0.255.7 VRF: default - Incorrect Transmit interval - Expected: 1200 Actual: 1300",
"Peer: 192.0.255.7 VRF: default - Incorrect Multiplier - Expected: 3 Actual: 4",
@ -283,354 +199,149 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyBFDSpecificPeers,
(VerifyBFDSpecificPeers, "success"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 108328132,
}
}
}
}
},
"MGMT": {
"ipv4Neighbors": {
"192.0.255.70": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 108328132,
}
}
}
}
},
"default": {"ipv4Neighbors": {"192.0.255.7": {"peerStats": {"": {"status": "up", "remoteDisc": 108328132}}}}},
"MGMT": {"ipv4Neighbors": {"192.0.255.70": {"peerStats": {"": {"status": "up", "remoteDisc": 108328132}}}}},
}
}
],
"inputs": {"bfd_peers": [{"peer_address": "192.0.255.7", "vrf": "default"}, {"peer_address": "192.0.255.70", "vrf": "MGMT"}]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-peer",
"test": VerifyBFDSpecificPeers,
(VerifyBFDSpecificPeers, "failure-no-peer"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 108328132,
}
}
}
}
},
"MGMT": {
"ipv4Neighbors": {
"192.0.255.71": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 108328132,
}
}
}
}
},
"default": {"ipv4Neighbors": {"192.0.255.7": {"peerStats": {"": {"status": "up", "remoteDisc": 108328132}}}}},
"MGMT": {"ipv4Neighbors": {"192.0.255.71": {"peerStats": {"": {"status": "up", "remoteDisc": 108328132}}}}},
}
}
],
"inputs": {"bfd_peers": [{"peer_address": "192.0.255.7", "vrf": "CS"}, {"peer_address": "192.0.255.70", "vrf": "MGMT"}]},
"expected": {
"result": "failure",
"messages": [
"Peer: 192.0.255.7 VRF: CS - Not found",
"Peer: 192.0.255.70 VRF: MGMT - Not found",
],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Peer: 192.0.255.7 VRF: CS - Not found", "Peer: 192.0.255.70 VRF: MGMT - Not found"]},
},
{
"name": "failure-session-down",
"test": VerifyBFDSpecificPeers,
(VerifyBFDSpecificPeers, "failure-session-down"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "down",
"remoteDisc": 108328132,
}
}
}
}
},
"MGMT": {
"ipv4Neighbors": {
"192.0.255.70": {
"peerStats": {
"": {
"status": "down",
"remoteDisc": 0,
}
}
}
}
},
"default": {"ipv4Neighbors": {"192.0.255.7": {"peerStats": {"": {"status": "down", "remoteDisc": 108328132}}}}},
"MGMT": {"ipv4Neighbors": {"192.0.255.70": {"peerStats": {"": {"status": "down", "remoteDisc": 0}}}}},
}
}
],
"inputs": {"bfd_peers": [{"peer_address": "192.0.255.7", "vrf": "default"}, {"peer_address": "192.0.255.70", "vrf": "MGMT"}]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 192.0.255.7 VRF: default - Session not properly established - State: down Remote Discriminator: 108328132",
"Peer: 192.0.255.70 VRF: MGMT - Session not properly established - State: down Remote Discriminator: 0",
],
},
},
{
"name": "success",
"test": VerifyBFDPeersHealth,
(VerifyBFDPeersHealth, "success"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 3940685114,
"lastDown": 1703657258.652725,
"l3intf": "",
}
}
},
"192.0.255.7": {"peerStats": {"": {"status": "up", "remoteDisc": 3940685114, "lastDown": 1703657258.652725, "l3intf": ""}}}
},
"ipv6Neighbors": {},
},
"MGMT": {
"ipv4Neighbors": {
"192.0.255.71": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 3940685114,
"lastDown": 1703657258.652725,
"l3intf": "",
}
}
},
"192.0.255.71": {"peerStats": {"": {"status": "up", "remoteDisc": 3940685114, "lastDown": 1703657258.652725, "l3intf": ""}}}
},
"ipv6Neighbors": {},
},
}
},
{
"utcTime": 1703667348.111288,
},
{"utcTime": 1703667348.111288},
],
"inputs": {"down_threshold": 2},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-peer",
"test": VerifyBFDPeersHealth,
(VerifyBFDPeersHealth, "failure-no-peer"): {
"eos_data": [
{
"vrfs": {
"MGMT": {
"ipv6Neighbors": {},
"ipv4Neighbors": {},
},
"default": {
"ipv6Neighbors": {},
"ipv4Neighbors": {},
},
}
},
{
"utcTime": 1703658481.8778424,
},
{"vrfs": {"MGMT": {"ipv6Neighbors": {}, "ipv4Neighbors": {}}, "default": {"ipv6Neighbors": {}, "ipv4Neighbors": {}}}},
{"utcTime": 1703658481.8778424},
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["No IPv4 BFD peers are configured for any VRF"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No IPv4 BFD peers are configured for any VRF"]},
},
{
"name": "failure-session-down",
"test": VerifyBFDPeersHealth,
(VerifyBFDPeersHealth, "failure-session-down"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "down",
"remoteDisc": 0,
"lastDown": 1703657258.652725,
"l3intf": "",
}
}
},
"192.0.255.70": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 3940685114,
"lastDown": 1703657258.652725,
"l3intf": "",
}
}
},
"192.0.255.7": {"peerStats": {"": {"status": "down", "remoteDisc": 0, "lastDown": 1703657258.652725, "l3intf": ""}}},
"192.0.255.70": {"peerStats": {"": {"status": "up", "remoteDisc": 3940685114, "lastDown": 1703657258.652725, "l3intf": ""}}},
},
"ipv6Neighbors": {},
},
"MGMT": {
"ipv4Neighbors": {
"192.0.255.71": {
"peerStats": {
"": {
"status": "down",
"remoteDisc": 0,
"lastDown": 1703657258.652725,
"l3intf": "",
}
}
},
},
"ipv4Neighbors": {"192.0.255.71": {"peerStats": {"": {"status": "down", "remoteDisc": 0, "lastDown": 1703657258.652725, "l3intf": ""}}}},
"ipv6Neighbors": {},
},
}
},
{
"utcTime": 1703658481.8778424,
},
{"utcTime": 1703658481.8778424},
],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 192.0.255.7 VRF: default - Session not properly established - State: down Remote Discriminator: 0",
"Peer: 192.0.255.71 VRF: MGMT - Session not properly established - State: down Remote Discriminator: 0",
],
},
},
{
"name": "failure-session-up-disc",
"test": VerifyBFDPeersHealth,
(VerifyBFDPeersHealth, "failure-session-up-disc"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 0,
"lastDown": 1703657258.652725,
"l3intf": "Ethernet2",
}
}
},
"192.0.255.71": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 0,
"lastDown": 1703657258.652725,
"l3intf": "Ethernet2",
}
}
},
"192.0.255.7": {"peerStats": {"": {"status": "up", "remoteDisc": 0, "lastDown": 1703657258.652725, "l3intf": "Ethernet2"}}},
"192.0.255.71": {"peerStats": {"": {"status": "up", "remoteDisc": 0, "lastDown": 1703657258.652725, "l3intf": "Ethernet2"}}},
},
"ipv6Neighbors": {},
}
}
},
{
"utcTime": 1703658481.8778424,
},
{"utcTime": 1703658481.8778424},
],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 192.0.255.7 VRF: default - Session not properly established - State: up Remote Discriminator: 0",
"Peer: 192.0.255.71 VRF: default - Session not properly established - State: up Remote Discriminator: 0",
],
},
},
{
"name": "failure-last-down",
"test": VerifyBFDPeersHealth,
(VerifyBFDPeersHealth, "failure-last-down"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 3940685114,
"lastDown": 1703657258.652725,
"l3intf": "",
}
}
},
"192.0.255.71": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 3940685114,
"lastDown": 1703657258.652725,
"l3intf": "",
}
}
},
"192.0.255.17": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 3940685114,
"lastDown": 1703657258.652725,
"l3intf": "",
}
}
},
"192.0.255.7": {"peerStats": {"": {"status": "up", "remoteDisc": 3940685114, "lastDown": 1703657258.652725, "l3intf": ""}}},
"192.0.255.71": {"peerStats": {"": {"status": "up", "remoteDisc": 3940685114, "lastDown": 1703657258.652725, "l3intf": ""}}},
"192.0.255.17": {"peerStats": {"": {"status": "up", "remoteDisc": 3940685114, "lastDown": 1703657258.652725, "l3intf": ""}}},
},
"ipv6Neighbors": {},
}
}
},
{
"utcTime": 1703667348.111288,
},
{"utcTime": 1703667348.111288},
],
"inputs": {"down_threshold": 4},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 192.0.255.7 VRF: default - Session failure detected within the expected uptime threshold (3 hours ago)",
"Peer: 192.0.255.71 VRF: default - Session failure detected within the expected uptime threshold (3 hours ago)",
@ -638,42 +349,18 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyBFDPeersRegProtocols,
(VerifyBFDPeersRegProtocols, "success"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 108328132,
"peerStatsDetail": {
"role": "active",
"apps": ["ospf"],
},
}
}
}
"192.0.255.7": {"peerStats": {"": {"status": "up", "remoteDisc": 108328132, "peerStatsDetail": {"role": "active", "apps": ["ospf"]}}}}
}
},
"MGMT": {
"ipv4Neighbors": {
"192.0.255.70": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 108328132,
"peerStatsDetail": {
"role": "active",
"apps": ["bgp"],
},
}
}
}
"192.0.255.70": {"peerStats": {"": {"status": "up", "remoteDisc": 108328132, "peerStatsDetail": {"role": "active", "apps": ["bgp"]}}}}
}
},
}
@ -685,43 +372,16 @@ DATA: list[dict[str, Any]] = [
{"peer_address": "192.0.255.70", "vrf": "MGMT", "protocols": ["bgp"]},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyBFDPeersRegProtocols,
(VerifyBFDPeersRegProtocols, "failure"): {
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"192.0.255.7": {
"peerStats": {
"": {
"status": "up",
"peerStatsDetail": {
"role": "active",
"apps": ["ospf"],
},
}
}
}
}
},
"default": {"ipv4Neighbors": {"192.0.255.7": {"peerStats": {"": {"status": "up", "peerStatsDetail": {"role": "active", "apps": ["ospf"]}}}}}},
"MGMT": {
"ipv4Neighbors": {
"192.0.255.70": {
"peerStats": {
"": {
"status": "up",
"remoteDisc": 0,
"peerStatsDetail": {
"role": "active",
"apps": ["bgp"],
},
}
}
}
"192.0.255.70": {"peerStats": {"": {"status": "up", "remoteDisc": 0, "peerStatsDetail": {"role": "active", "apps": ["bgp"]}}}}
}
},
}
@ -734,36 +394,21 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 192.0.255.7 VRF: default - `isis` routing protocol(s) not configured",
"Peer: 192.0.255.70 VRF: MGMT - `isis`, `ospf` routing protocol(s) not configured",
],
},
},
{
"name": "failure-not-found",
"test": VerifyBFDPeersRegProtocols,
"eos_data": [
{
"vrfs": {
"default": {},
"MGMT": {},
}
}
],
(VerifyBFDPeersRegProtocols, "failure-not-found"): {
"eos_data": [{"vrfs": {"default": {}, "MGMT": {}}}],
"inputs": {
"bfd_peers": [
{"peer_address": "192.0.255.7", "vrf": "default", "protocols": ["isis"]},
{"peer_address": "192.0.255.70", "vrf": "MGMT", "protocols": ["isis"]},
]
},
"expected": {
"result": "failure",
"messages": [
"Peer: 192.0.255.7 VRF: default - Not found",
"Peer: 192.0.255.70 VRF: MGMT - Not found",
],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Peer: 192.0.255.7 VRF: default - Not found", "Peer: 192.0.255.70 VRF: MGMT - Not found"]},
},
]
}

View file

@ -5,59 +5,34 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.configuration import VerifyRunningConfigDiffs, VerifyRunningConfigLines, VerifyZeroTouch
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyZeroTouch,
"eos_data": [{"mode": "disabled"}],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyZeroTouch,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyZeroTouch, "success"): {"eos_data": [{"mode": "disabled"}], "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyZeroTouch, "failure"): {
"eos_data": [{"mode": "enabled"}],
"inputs": None,
"expected": {"result": "failure", "messages": ["ZTP is NOT disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["ZTP is NOT disabled"]},
},
{
"name": "success",
"test": VerifyRunningConfigDiffs,
"eos_data": [""],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyRunningConfigDiffs,
"eos_data": ["blah blah"],
"inputs": None,
"expected": {"result": "failure", "messages": ["blah blah"]},
},
{
"name": "success",
"test": VerifyRunningConfigLines,
"eos_data": ["blah blah"],
"inputs": {"regex_patterns": ["blah"]},
"expected": {"result": "success"},
},
{
"name": "success",
"test": VerifyRunningConfigLines,
(VerifyRunningConfigDiffs, "success"): {"eos_data": [""], "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyRunningConfigDiffs, "failure"): {"eos_data": ["blah blah"], "expected": {"result": AntaTestStatus.FAILURE, "messages": ["blah blah"]}},
(VerifyRunningConfigLines, "success"): {"eos_data": ["blah blah"], "inputs": {"regex_patterns": ["blah"]}, "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyRunningConfigLines, "success-patterns"): {
"eos_data": ["enable password something\nsome other line"],
"inputs": {"regex_patterns": ["^enable password .*$", "^.*other line$"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyRunningConfigLines,
(VerifyRunningConfigLines, "failure"): {
"eos_data": ["enable password something\nsome other line"],
"inputs": {"regex_patterns": ["bla", "bleh"]},
"expected": {"result": "failure", "messages": ["Following patterns were not found: 'bla', 'bleh'"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Following patterns were not found: 'bla', 'bleh"]},
},
]
}

View file

@ -5,313 +5,244 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.connectivity import VerifyLLDPNeighbors, VerifyReachability
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success-ip",
"test": VerifyReachability,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyReachability, "success-ip"): {
"inputs": {"hosts": [{"destination": "10.0.0.1", "source": "10.0.0.5"}, {"destination": "10.0.0.2", "source": "10.0.0.5"}]},
"eos_data": [
{
"messages": [
"""PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.
80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms
80 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.072 ms
--- 10.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
""",
],
"PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.\n 80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms\n"
" 80 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.072 ms\n\n --- 10.0.0.1 ping statistics ---\n"
" 2 packets transmitted, 2 received, 0% packet loss, time 0ms\n rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms,"
" ipg/ewma 0.370/0.225 ms\n\n "
]
},
{
"messages": [
"""PING 10.0.0.2 (10.0.0.2) from 10.0.0.5 : 72(100) bytes of data.
80 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.247 ms
80 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.072 ms
--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
""",
],
"PING 10.0.0.2 (10.0.0.2) from 10.0.0.5 : 72(100) bytes of data.\n 80 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.247 ms\n"
" 80 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.072 ms\n\n --- 10.0.0.2 ping statistics ---\n"
" 2 packets transmitted, 2 received, 0% packet loss, time 0ms\n rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms,"
" ipg/ewma 0.370/0.225 ms\n\n "
]
},
],
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-expected-unreachable",
"test": VerifyReachability,
(VerifyReachability, "success-expected-unreachable"): {
"eos_data": [
{
"messages": [
"""PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.
--- 10.0.0.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 10ms
""",
],
},
"PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.\n\n --- 10.0.0.1 ping statistics ---\n"
" 2 packets transmitted, 0 received, 100% packet loss, time 10ms\n "
]
}
],
"inputs": {"hosts": [{"destination": "10.0.0.1", "source": "10.0.0.5", "reachable": False}]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-ipv6",
"test": VerifyReachability,
(VerifyReachability, "success-ipv6"): {
"eos_data": [
{
"messages": [
"""PING fd12:3456:789a:1::2(fd12:3456:789a:1::2) from fd12:3456:789a:1::1 : 52 data bytes
60 bytes from fd12:3456:789a:1::2: icmp_seq=1 ttl=64 time=0.097 ms
60 bytes from fd12:3456:789a:1::2: icmp_seq=2 ttl=64 time=0.033 ms
--- fd12:3456:789a:1::2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.033/0.065/0.097/0.032 ms, ipg/ewma 0.148/0.089 ms
""",
],
},
"PING fd12:3456:789a:1::2(fd12:3456:789a:1::2) from fd12:3456:789a:1::1 : 52 data bytes\n 60 bytes from fd12:3456:789a:1::2:"
" icmp_seq=1 ttl=64 time=0.097 ms\n 60 bytes from fd12:3456:789a:1::2: icmp_seq=2 ttl=64 time=0.033 ms\n\n"
" --- fd12:3456:789a:1::2 ping statistics ---\n 2 packets transmitted, 2 received, 0% packet loss, time 0ms\n"
" rtt min/avg/max/mdev = 0.033/0.065/0.097/0.032 ms, ipg/ewma 0.148/0.089 ms\n "
]
}
],
"inputs": {"hosts": [{"destination": "fd12:3456:789a:1::2", "source": "fd12:3456:789a:1::1"}]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-ipv6-vlan",
"test": VerifyReachability,
(VerifyReachability, "success-ipv6-vlan"): {
"eos_data": [
{
"messages": [
"""PING fd12:3456:789a:1::2(fd12:3456:789a:1::2) 52 data bytes
60 bytes from fd12:3456:789a:1::2: icmp_seq=1 ttl=64 time=0.094 ms
60 bytes from fd12:3456:789a:1::2: icmp_seq=2 ttl=64 time=0.027 ms
--- fd12:3456:789a:1::2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.027/0.060/0.094/0.033 ms, ipg/ewma 0.152/0.085 ms
""",
],
},
"PING fd12:3456:789a:1::2(fd12:3456:789a:1::2) 52 data bytes\n 60 bytes from fd12:3456:789a:1::2: "
"icmp_seq=1 ttl=64 time=0.094 ms\n 60 bytes from fd12:3456:789a:1::2: icmp_seq=2 ttl=64 time=0.027 ms\n\n"
" --- fd12:3456:789a:1::2 ping statistics ---\n 2 packets transmitted, 2 received, 0% packet loss,"
" time 0ms\n rtt min/avg/max/mdev = 0.027/0.060/0.094/0.033 ms, ipg/ewma 0.152/0.085 ms\n "
]
}
],
"inputs": {"hosts": [{"destination": "fd12:3456:789a:1::2", "source": "vl110"}]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-interface",
"test": VerifyReachability,
(VerifyReachability, "success-interface"): {
"inputs": {"hosts": [{"destination": "10.0.0.1", "source": "Management0"}, {"destination": "10.0.0.2", "source": "Management0"}]},
"eos_data": [
{
"messages": [
"""PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.
80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms
80 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.072 ms
--- 10.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
""",
],
"PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.\n 80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms\n"
" 80 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.072 ms\n\n --- 10.0.0.1 ping statistics ---\n"
" 2 packets transmitted, 2 received, 0% packet loss, time 0ms\n rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms,"
" ipg/ewma 0.370/0.225 ms\n\n "
]
},
{
"messages": [
"""PING 10.0.0.2 (10.0.0.2) from 10.0.0.5 : 72(100) bytes of data.
80 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.247 ms
80 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.072 ms
--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
""",
],
"PING 10.0.0.2 (10.0.0.2) from 10.0.0.5 : 72(100) bytes of data.\n 80 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.247 ms\n"
" 80 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.072 ms\n\n --- 10.0.0.2 ping statistics ---\n"
" 2 packets transmitted, 2 received, 0% packet loss, time 0ms\n rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms,"
" ipg/ewma 0.370/0.225 ms\n\n "
]
},
],
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-repeat",
"test": VerifyReachability,
(VerifyReachability, "success-repeat"): {
"inputs": {"hosts": [{"destination": "10.0.0.1", "source": "Management0", "repeat": 1}]},
"eos_data": [
{
"messages": [
"""PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.
80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms
--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
""",
],
},
"PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.\n 80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms\n\n"
" --- 10.0.0.1 ping statistics ---\n 1 packets transmitted, 1 received, 0% packet loss, time 0ms\n"
" rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms\n\n "
]
}
],
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-df-bit-size",
"test": VerifyReachability,
(VerifyReachability, "success-df-bit-size"): {
"inputs": {"hosts": [{"destination": "10.0.0.1", "source": "Management0", "repeat": 5, "size": 1500, "df_bit": True}]},
"eos_data": [
{
"messages": [
"""PING 10.0.0.1 (10.0.0.1) from 172.20.20.6 : 1472(1500) bytes of data.
1480 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.085 ms
1480 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.020 ms
1480 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.019 ms
1480 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=0.018 ms
1480 bytes from 10.0.0.1: icmp_seq=5 ttl=64 time=0.017 ms
--- 10.0.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.017/0.031/0.085/0.026 ms, ipg/ewma 0.061/0.057 ms""",
],
},
"PING 10.0.0.1 (10.0.0.1) from 172.20.20.6 : 1472(1500) bytes of data.\n 1480 bytes from 10.0.0.1: "
"icmp_seq=1 ttl=64 time=0.085 ms\n 1480 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.020 ms\n"
" 1480 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.019 ms\n 1480 bytes from 10.0.0.1:"
" icmp_seq=4 ttl=64 time=0.018 ms\n 1480 bytes from 10.0.0.1:"
" icmp_seq=5 ttl=64 time=0.017 ms\n\n --- 10.0.0.1 ping statistics ---\n 5 packets transmitted,"
" 5 received, 0% packet loss, time 0ms\n rtt min/avg/max/mdev = 0.017/0.031/0.085/0.026 ms, ipg/ewma 0.061/0.057 ms"
]
}
],
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-ip",
"test": VerifyReachability,
(VerifyReachability, "success-without-source"): {
"inputs": {"hosts": [{"destination": "10.0.0.1", "repeat": 1}]},
"eos_data": [
{
"messages": [
"PING 10.0.0.1 (10.0.0.1) : 72(100) bytes of data.\n 80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms\n\n"
" --- 10.0.0.1 ping statistics ---\n 1 packets transmitted, 1 received, 0% packet loss, time 0ms\n"
" rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms\n\n "
]
}
],
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyReachability, "failure-ip"): {
"inputs": {"hosts": [{"destination": "10.0.0.11", "source": "10.0.0.5"}, {"destination": "10.0.0.2", "source": "10.0.0.5"}]},
"eos_data": [
{
"messages": [
"""ping: sendmsg: Network is unreachable
ping: sendmsg: Network is unreachable
PING 10.0.0.11 (10.0.0.11) from 10.0.0.5 : 72(100) bytes of data.
--- 10.0.0.11 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 10ms
""",
],
"ping: sendmsg: Network is unreachable\n ping: sendmsg: Network is unreachable\n "
"PING 10.0.0.11 (10.0.0.11) from 10.0.0.5 : 72(100) bytes of data.\n\n --- 10.0.0.11 ping statistics ---\n"
" 2 packets transmitted, 0 received, 100% packet loss, time 10ms\n\n\n "
]
},
{
"messages": [
"""PING 10.0.0.2 (10.0.0.2) from 10.0.0.5 : 72(100) bytes of data.
80 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.247 ms
80 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.072 ms
--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
""",
],
"PING 10.0.0.2 (10.0.0.2) from 10.0.0.5 : 72(100) bytes of data.\n 80 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.247 ms\n"
" 80 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.072 ms\n\n --- 10.0.0.2 ping statistics ---\n "
"2 packets transmitted, 2 received, 0% packet loss, time 0ms\n rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms,"
" ipg/ewma 0.370/0.225 ms\n\n "
]
},
],
"expected": {"result": "failure", "messages": ["Host: 10.0.0.11 Source: 10.0.0.5 VRF: default - Unreachable"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Host: 10.0.0.11 Source: 10.0.0.5 VRF: default - Unreachable"]},
},
{
"name": "failure-ipv6",
"test": VerifyReachability,
(VerifyReachability, "failure-ipv6"): {
"eos_data": [
{
"messages": [
"""PING fd12:3456:789a:1::2(fd12:3456:789a:1::2) from fd12:3456:789a:1::1 : 52 data bytes
--- fd12:3456:789a:1::3 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 10ms
""",
],
},
"PING fd12:3456:789a:1::2(fd12:3456:789a:1::2) from fd12:3456:789a:1::1 : 52 data bytes\n\n --- fd12:3456:789a:1::3 "
"ping statistics ---\n 2 packets transmitted, 0 received, 100% packet loss, time 10ms\n "
]
}
],
"inputs": {"hosts": [{"destination": "fd12:3456:789a:1::2", "source": "fd12:3456:789a:1::1"}]},
"expected": {"result": "failure", "messages": ["Host: fd12:3456:789a:1::2 Source: fd12:3456:789a:1::1 VRF: default - Unreachable"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Host: fd12:3456:789a:1::2 Source: fd12:3456:789a:1::1 VRF: default - Unreachable"]},
},
{
"name": "failure-interface",
"test": VerifyReachability,
(VerifyReachability, "failure-interface"): {
"inputs": {"hosts": [{"destination": "10.0.0.11", "source": "Management0"}, {"destination": "10.0.0.2", "source": "Management0"}]},
"eos_data": [
{
"messages": [
"""ping: sendmsg: Network is unreachable
ping: sendmsg: Network is unreachable
PING 10.0.0.11 (10.0.0.11) from 10.0.0.5 : 72(100) bytes of data.
--- 10.0.0.11 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 10ms
""",
],
"ping: sendmsg: Network is unreachable\n ping: sendmsg: Network is unreachable\n "
"PING 10.0.0.11 (10.0.0.11) from 10.0.0.5 : 72(100) bytes of data.\n\n --- 10.0.0.11 ping statistics ---\n"
" 2 packets transmitted, 0 received, 100% packet loss, time 10ms\n\n\n "
]
},
{
"messages": [
"""PING 10.0.0.2 (10.0.0.2) from 10.0.0.5 : 72(100) bytes of data.
80 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.247 ms
80 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.072 ms
--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
""",
],
"PING 10.0.0.2 (10.0.0.2) from 10.0.0.5 : 72(100) bytes of data.\n 80 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.247 ms\n"
" 80 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.072 ms\n\n --- 10.0.0.2 ping statistics ---\n"
" 2 packets transmitted, 2 received, 0% packet loss, time 0ms\n rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms,"
" ipg/ewma 0.370/0.225 ms\n\n "
]
},
],
"expected": {"result": "failure", "messages": ["Host: 10.0.0.11 Source: Management0 VRF: default - Unreachable"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Host: 10.0.0.11 Source: Management0 VRF: default - Unreachable"]},
},
{
"name": "failure-size",
"test": VerifyReachability,
(VerifyReachability, "failure-size"): {
"inputs": {"hosts": [{"destination": "10.0.0.1", "source": "Management0", "repeat": 5, "size": 1501, "df_bit": True}]},
"eos_data": [
{
"messages": [
"""PING 10.0.0.1 (10.0.0.1) from 172.20.20.6 : 1473(1501) bytes of data.
ping: local error: message too long, mtu=1500
ping: local error: message too long, mtu=1500
ping: local error: message too long, mtu=1500
ping: local error: message too long, mtu=1500
ping: local error: message too long, mtu=1500
--- 10.0.0.1 ping statistics ---
5 packets transmitted, 0 received, +5 errors, 100% packet loss, time 40ms
""",
],
},
"PING 10.0.0.1 (10.0.0.1) from 172.20.20.6 : 1473(1501) bytes of data.\n ping: local error: message too long, mtu=1500\n"
" ping: local error: message too long, mtu=1500\n"
" ping: local error: message too long, mtu=1500\n ping: local error: message too long, mtu=1500\n"
" ping: local error: message too long, mtu=1500\n\n --- 10.0.0.1 ping statistics ---\n"
" 5 packets transmitted, 0 received, +5 errors, 100% packet loss, time 40ms\n "
]
}
],
"expected": {"result": "failure", "messages": ["Host: 10.0.0.1 Source: Management0 VRF: default - Unreachable"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Host: 10.0.0.1 Source: Management0 VRF: default - Unreachable"]},
},
{
"name": "failure-expected-unreachable",
"test": VerifyReachability,
(VerifyReachability, "failure-expected-unreachable"): {
"eos_data": [
{
"messages": [
"""PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.
80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms
80 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.072 ms
--- 10.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms, ipg/ewma 0.370/0.225 ms
""",
],
},
"PING 10.0.0.1 (10.0.0.1) from 10.0.0.5 : 72(100) bytes of data.\n 80 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.247 ms\n"
" 80 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.072 ms\n\n --- 10.0.0.1 ping statistics ---\n"
" 2 packets transmitted, 2 received, 0% packet loss, time 0ms\n rtt min/avg/max/mdev = 0.072/0.159/0.247/0.088 ms,"
" ipg/ewma 0.370/0.225 ms\n\n "
]
}
],
"inputs": {"hosts": [{"destination": "10.0.0.1", "source": "10.0.0.5", "reachable": False}]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Host: 10.0.0.1 Source: 10.0.0.5 VRF: default - Destination is expected to be unreachable but found reachable"],
},
},
{
"name": "success",
"test": VerifyLLDPNeighbors,
(VerifyReachability, "failure-without-source"): {
"inputs": {"hosts": [{"destination": "10.0.0.1", "repeat": 1}]},
"eos_data": [
{
"messages": [
"ping: sendmsg: Network is unreachable\n PING 10.0.0.1 (10.0.0.1) : 72(100) bytes of data.\n\n"
" --- 10.0.0.11 ping statistics ---\n "
"2 packets transmitted, 0 received, 100% packet loss, time 10ms\n\n "
]
}
],
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Host: 10.0.0.1 VRF: default - Unreachable"]},
},
(VerifyLLDPNeighbors, "success"): {
"eos_data": [
{
"lldpNeighbors": {
@ -323,12 +254,12 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE1",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet1",
},
},
],
}
]
},
"Ethernet2": {
"lldpNeighborInfo": [
@ -338,27 +269,25 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE2",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet2",
},
},
],
}
]
},
},
},
}
}
],
"inputs": {
"neighbors": [
{"port": "Ethernet1", "neighbor_device": "DC1-SPINE1", "neighbor_port": "Ethernet1"},
{"port": "Ethernet2", "neighbor_device": "DC1-SPINE2", "neighbor_port": "Ethernet1"},
],
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-multiple-neighbors",
"test": VerifyLLDPNeighbors,
(VerifyLLDPNeighbors, "success-multiple-neighbors"): {
"eos_data": [
{
"lldpNeighbors": {
@ -370,7 +299,7 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE1",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet1",
},
@ -381,26 +310,20 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE2",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet2",
},
},
],
},
},
},
]
}
}
}
],
"inputs": {
"neighbors": [
{"port": "Ethernet1", "neighbor_device": "DC1-SPINE2", "neighbor_port": "Ethernet1"},
],
},
"expected": {"result": "success"},
"inputs": {"neighbors": [{"port": "Ethernet1", "neighbor_device": "DC1-SPINE2", "neighbor_port": "Ethernet1"}]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-port-not-configured",
"test": VerifyLLDPNeighbors,
(VerifyLLDPNeighbors, "failure-port-not-configured"): {
"eos_data": [
{
"lldpNeighbors": {
@ -412,27 +335,25 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE1",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet1",
},
},
],
},
},
},
}
]
}
}
}
],
"inputs": {
"neighbors": [
{"port": "Ethernet1", "neighbor_device": "DC1-SPINE1", "neighbor_port": "Ethernet1"},
{"port": "Ethernet2", "neighbor_device": "DC1-SPINE2", "neighbor_port": "Ethernet1"},
],
]
},
"expected": {"result": "failure", "messages": ["Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - Port not found"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - Port not found"]},
},
{
"name": "failure-no-neighbor",
"test": VerifyLLDPNeighbors,
(VerifyLLDPNeighbors, "failure-no-neighbor"): {
"eos_data": [
{
"lldpNeighbors": {
@ -444,28 +365,26 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE1",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet1",
},
},
],
}
]
},
"Ethernet2": {"lldpNeighborInfo": []},
},
},
}
}
],
"inputs": {
"neighbors": [
{"port": "Ethernet1", "neighbor_device": "DC1-SPINE1", "neighbor_port": "Ethernet1"},
{"port": "Ethernet2", "neighbor_device": "DC1-SPINE2", "neighbor_port": "Ethernet1"},
],
]
},
"expected": {"result": "failure", "messages": ["Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - No LLDP neighbors"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - No LLDP neighbors"]},
},
{
"name": "failure-wrong-neighbor",
"test": VerifyLLDPNeighbors,
(VerifyLLDPNeighbors, "failure-wrong-neighbor"): {
"eos_data": [
{
"lldpNeighbors": {
@ -477,12 +396,12 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE1",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet1",
},
},
],
}
]
},
"Ethernet2": {
"lldpNeighborInfo": [
@ -492,30 +411,28 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE2",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet2"',
"interfaceId": "Ethernet2",
"interfaceId_v2": "Ethernet2",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet2",
},
},
],
}
]
},
},
},
}
}
],
"inputs": {
"neighbors": [
{"port": "Ethernet1", "neighbor_device": "DC1-SPINE1", "neighbor_port": "Ethernet1"},
{"port": "Ethernet2", "neighbor_device": "DC1-SPINE2", "neighbor_port": "Ethernet1"},
],
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: DC1-SPINE2/Ethernet2"],
},
},
{
"name": "failure-multiple",
"test": VerifyLLDPNeighbors,
(VerifyLLDPNeighbors, "failure-multiple"): {
"eos_data": [
{
"lldpNeighbors": {
@ -527,26 +444,26 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE1",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet2"',
"interfaceId": "Ethernet2",
"interfaceId_v2": "Ethernet2",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet1",
},
},
],
}
]
},
"Ethernet2": {"lldpNeighborInfo": []},
},
},
}
}
],
"inputs": {
"neighbors": [
{"port": "Ethernet1", "neighbor_device": "DC1-SPINE1", "neighbor_port": "Ethernet1"},
{"port": "Ethernet2", "neighbor_device": "DC1-SPINE2", "neighbor_port": "Ethernet1"},
{"port": "Ethernet3", "neighbor_device": "DC1-SPINE3", "neighbor_port": "Ethernet1"},
],
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Port: Ethernet1 Neighbor: DC1-SPINE1 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: DC1-SPINE1/Ethernet2",
"Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - No LLDP neighbors",
@ -554,9 +471,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-multiple-neighbors",
"test": VerifyLLDPNeighbors,
(VerifyLLDPNeighbors, "failure-multiple-neighbors"): {
"eos_data": [
{
"lldpNeighbors": {
@ -568,7 +483,7 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE1",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet1",
},
@ -579,24 +494,20 @@ DATA: list[dict[str, Any]] = [
"systemName": "DC1-SPINE2",
"neighborInterfaceInfo": {
"interfaceIdType": "interfaceName",
"interfaceId": '"Ethernet1"',
"interfaceId": "Ethernet1",
"interfaceId_v2": "Ethernet1",
"interfaceDescription": "P2P_LINK_TO_DC1-LEAF1A_Ethernet2",
},
},
],
},
},
},
]
}
}
}
],
"inputs": {
"neighbors": [
{"port": "Ethernet1", "neighbor_device": "DC1-SPINE3", "neighbor_port": "Ethernet1"},
],
},
"inputs": {"neighbors": [{"port": "Ethernet1", "neighbor_device": "DC1-SPINE3", "neighbor_port": "Ethernet1"}]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Port: Ethernet1 Neighbor: DC1-SPINE3 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: DC1-SPINE1/Ethernet1, DC1-SPINE2/Ethernet1"],
},
},
]
}

View file

@ -5,182 +5,127 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.cvx import VerifyActiveCVXConnections, VerifyCVXClusterStatus, VerifyManagementCVX, VerifyMcsClientMounts, VerifyMcsServerMounts
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyMcsClientMounts,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyMcsClientMounts, "success"): {
"eos_data": [{"mountStates": [{"path": "mcs/v1/toSwitch/28-99-3a-8f-93-7b", "type": "Mcs::DeviceConfigV1", "state": "mountStateMountComplete"}]}],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-haclient",
"test": VerifyMcsClientMounts,
(VerifyMcsClientMounts, "success-haclient"): {
"eos_data": [
{
"mountStates": [
{"path": "mcs/v1/apiCfgRedState", "type": "Mcs::ApiConfigRedundancyState", "state": "mountStateMountComplete"},
{"path": "mcs/v1/toSwitch/00-1c-73-74-c0-8b", "type": "Mcs::DeviceConfigV1", "state": "mountStateMountComplete"},
]
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-partial-non-mcs",
"test": VerifyMcsClientMounts,
(VerifyMcsClientMounts, "success-partial-non-mcs"): {
"eos_data": [
{
"mountStates": [
{"path": "blah/blah/blah", "type": "blah::blah", "state": "mountStatePreservedUnmounted"},
{"path": "mcs/v1/toSwitch/00-1c-73-74-c0-8b", "type": "Mcs::DeviceConfigV1", "state": "mountStateMountComplete"},
]
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-nomounts",
"test": VerifyMcsClientMounts,
"eos_data": [
{"mountStates": []},
],
"inputs": None,
"expected": {"result": "failure", "messages": ["MCS Client mount states are not present"]},
(VerifyMcsClientMounts, "failure-nomounts"): {
"eos_data": [{"mountStates": []}],
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["MCS Client mount states are not present"]},
},
{
"name": "failure-mountStatePreservedUnmounted",
"test": VerifyMcsClientMounts,
(VerifyMcsClientMounts, "failure-mountStatePreservedUnmounted"): {
"eos_data": [{"mountStates": [{"path": "mcs/v1/toSwitch/28-99-3a-8f-93-7b", "type": "Mcs::DeviceConfigV1", "state": "mountStatePreservedUnmounted"}]}],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["MCS Client mount states are not valid - Expected: mountStateMountComplete Actual: mountStatePreservedUnmounted"],
},
},
{
"name": "failure-partial-haclient",
"test": VerifyMcsClientMounts,
(VerifyMcsClientMounts, "failure-partial-haclient"): {
"eos_data": [
{
"mountStates": [
{"path": "mcs/v1/apiCfgRedState", "type": "Mcs::ApiConfigRedundancyState", "state": "mountStateMountComplete"},
{"path": "mcs/v1/toSwitch/00-1c-73-74-c0-8b", "type": "Mcs::DeviceConfigV1", "state": "mountStatePreservedUnmounted"},
]
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["MCS Client mount states are not valid - Expected: mountStateMountComplete Actual: mountStatePreservedUnmounted"],
},
},
{
"name": "failure-full-haclient",
"test": VerifyMcsClientMounts,
(VerifyMcsClientMounts, "failure-full-haclient"): {
"eos_data": [
{
"mountStates": [
{"path": "blah/blah/blah", "type": "blah::blahState", "state": "mountStatePreservedUnmounted"},
{"path": "mcs/v1/toSwitch/00-1c-73-74-c0-8b", "type": "Mcs::DeviceConfigV1", "state": "mountStatePreservedUnmounted"},
]
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["MCS Client mount states are not valid - Expected: mountStateMountComplete Actual: mountStatePreservedUnmounted"],
},
},
{
"name": "failure-non-mcs-client",
"test": VerifyMcsClientMounts,
"eos_data": [
{"mountStates": [{"path": "blah/blah/blah", "type": "blah::blahState", "state": "mountStatePreservedUnmounted"}]},
],
"inputs": None,
"expected": {"result": "failure", "messages": ["MCS Client mount states are not present"]},
(VerifyMcsClientMounts, "failure-non-mcs-client"): {
"eos_data": [{"mountStates": [{"path": "blah/blah/blah", "type": "blah::blahState", "state": "mountStatePreservedUnmounted"}]}],
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["MCS Client mount states are not present"]},
},
{
"name": "failure-partial-mcs-client",
"test": VerifyMcsClientMounts,
(VerifyMcsClientMounts, "failure-partial-mcs-client"): {
"eos_data": [
{
"mountStates": [
{"path": "blah/blah/blah", "type": "blah::blahState", "state": "mountStatePreservedUnmounted"},
{"path": "blah/blah/blah", "type": "Mcs::DeviceConfigV1", "state": "mountStatePreservedUnmounted"},
]
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["MCS Client mount states are not valid - Expected: mountStateMountComplete Actual: mountStatePreservedUnmounted"],
},
},
{
"name": "success-enabled",
"test": VerifyManagementCVX,
"eos_data": [
{
"clusterStatus": {
"enabled": True,
}
}
],
(VerifyManagementCVX, "success-enabled"): {
"eos_data": [{"clusterStatus": {"enabled": True}}],
"inputs": {"enabled": True},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-disabled",
"test": VerifyManagementCVX,
"eos_data": [
{
"clusterStatus": {
"enabled": False,
}
}
],
(VerifyManagementCVX, "success-disabled"): {
"eos_data": [{"clusterStatus": {"enabled": False}}],
"inputs": {"enabled": False},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-invalid-state",
"test": VerifyManagementCVX,
"eos_data": [
{
"clusterStatus": {
"enabled": False,
}
}
],
(VerifyManagementCVX, "failure-invalid-state"): {
"eos_data": [{"clusterStatus": {"enabled": False}}],
"inputs": {"enabled": True},
"expected": {"result": "failure", "messages": ["Management CVX status is not valid: Expected: enabled Actual: disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Management CVX status is not valid: Expected: enabled Actual: disabled"]},
},
{
"name": "failure-no-enabled state",
"test": VerifyManagementCVX,
(VerifyManagementCVX, "failure-no-enabled state"): {
"eos_data": [{"clusterStatus": {}}],
"inputs": {"enabled": False},
"expected": {"result": "failure", "messages": ["Management CVX status - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Management CVX status - Not configured"]},
},
{
"name": "failure - no clusterStatus",
"test": VerifyManagementCVX,
(VerifyManagementCVX, "failure - no clusterStatus"): {
"eos_data": [{}],
"inputs": {"enabled": False},
"expected": {"result": "failure", "messages": ["Management CVX status - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Management CVX status - Not configured"]},
},
{
"name": "success",
"test": VerifyMcsServerMounts,
(VerifyMcsServerMounts, "success"): {
"eos_data": [
{
"connections": [
@ -205,21 +150,17 @@ DATA: list[dict[str, Any]] = [
}
],
"inputs": {"connections_count": 1},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-mounts",
"test": VerifyMcsServerMounts,
(VerifyMcsServerMounts, "failure-no-mounts"): {
"eos_data": [{"connections": [{"hostname": "media-leaf-1", "mounts": []}]}],
"inputs": {"connections_count": 1},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Host: media-leaf-1 - No mount status found", "Incorrect CVX successful connections count - Expected: 1 Actual: 0"],
},
},
{
"name": "failure-unexpected-number-paths",
"test": VerifyMcsServerMounts,
(VerifyMcsServerMounts, "failure-unexpected-number-paths"): {
"eos_data": [
{
"connections": [
@ -244,17 +185,15 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {"connections_count": 1},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Host: media-leaf-1 - Incorrect number of mount path states - Expected: 3 Actual: 2",
"Host: media-leaf-1 - Unexpected MCS path type - Expected: Mcs::ApiConfigRedundancyStatus, Mcs::ActiveFlows, "
"Mcs::Client::Status Actual: Mcs::ApiStatus",
"Host: media-leaf-1 - Unexpected MCS path type - Expected: Mcs::ApiConfigRedundancyStatus, "
"Mcs::ActiveFlows, Mcs::Client::Status Actual: Mcs::ApiStatus",
],
},
},
{
"name": "failure-unexpected-path-type",
"test": VerifyMcsServerMounts,
(VerifyMcsServerMounts, "failure-unexpected-path-type"): {
"eos_data": [
{
"connections": [
@ -280,16 +219,14 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {"connections_count": 1},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Host: media-leaf-1 - Unexpected MCS path type - Expected: Mcs::ApiConfigRedundancyStatus, Mcs::ActiveFlows, Mcs::Client::Status"
" Actual: Mcs::ApiStatus"
"Host: media-leaf-1 - Unexpected MCS path type - Expected: Mcs::ApiConfigRedundancyStatus, Mcs::ActiveFlows, "
"Mcs::Client::Status Actual: Mcs::ApiStatus"
],
},
},
{
"name": "failure-invalid-mount-state",
"test": VerifyMcsServerMounts,
(VerifyMcsServerMounts, "failure-invalid-mount-state"): {
"eos_data": [
{
"connections": [
@ -315,16 +252,14 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {"connections_count": 1},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Host: media-leaf-1 Path Type: Mcs::ApiConfigRedundancyStatus - MCS server mount state is not valid - Expected: mountStateMountComplete"
" Actual:mountStateMountFailed"
"Host: media-leaf-1 Path Type: Mcs::ApiConfigRedundancyStatus - MCS server mount state is not valid - "
"Expected: mountStateMountComplete Actual:mountStateMountFailed"
],
},
},
{
"name": "failure-no-mcs-mount",
"test": VerifyMcsServerMounts,
(VerifyMcsServerMounts, "failure-no-mcs-mount"): {
"eos_data": [
{
"connections": [
@ -341,69 +276,46 @@ DATA: list[dict[str, Any]] = [
}
],
"inputs": {"connections_count": 1},
"expected": {"result": "failure", "messages": ["MCS mount state not detected", "Incorrect CVX successful connections count - Expected: 1 Actual: 0"]},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["MCS mount state not detected", "Incorrect CVX successful connections count - Expected: 1 Actual: 0"],
},
},
{
"name": "failure-connections",
"test": VerifyMcsServerMounts,
(VerifyMcsServerMounts, "failure-connections"): {
"eos_data": [{}],
"inputs": {"connections_count": 1},
"expected": {"result": "failure", "messages": ["CVX connections are not available"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["CVX connections are not available"]},
},
{
"name": "success",
"test": VerifyActiveCVXConnections,
(VerifyActiveCVXConnections, "success"): {
"eos_data": [
{
"connections": [
{
"switchId": "fc:bd:67:c3:16:55",
"hostname": "lyv563",
"oobConnectionActive": True,
},
{
"switchId": "00:1c:73:3c:e3:9e",
"hostname": "tg264",
"oobConnectionActive": True,
},
{"switchId": "fc:bd:67:c3:16:55", "hostname": "lyv563", "oobConnectionActive": True},
{"switchId": "00:1c:73:3c:e3:9e", "hostname": "tg264", "oobConnectionActive": True},
]
}
],
"inputs": {"connections_count": 2},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyActiveCVXConnections,
(VerifyActiveCVXConnections, "failure"): {
"eos_data": [
{
"connections": [
{
"switchId": "fc:bd:67:c3:16:55",
"hostname": "lyv563",
"oobConnectionActive": False,
},
{
"switchId": "00:1c:73:3c:e3:9e",
"hostname": "tg264",
"oobConnectionActive": True,
},
{"switchId": "fc:bd:67:c3:16:55", "hostname": "lyv563", "oobConnectionActive": False},
{"switchId": "00:1c:73:3c:e3:9e", "hostname": "tg264", "oobConnectionActive": True},
]
}
],
"inputs": {"connections_count": 2},
"expected": {"result": "failure", "messages": ["CVX active connections count - Expected: 2 Actual: 1"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["CVX active connections count - Expected: 2 Actual: 1"]},
},
{
"name": "failure-no-connections",
"test": VerifyActiveCVXConnections,
(VerifyActiveCVXConnections, "failure-no-connections"): {
"eos_data": [{}],
"inputs": {"connections_count": 2},
"expected": {"result": "failure", "messages": ["CVX connections are not available"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["CVX connections are not available"]},
},
{
"name": "success-all",
"test": VerifyCVXClusterStatus,
(VerifyCVXClusterStatus, "success-all"): {
"eos_data": [
{
"enabled": True,
@ -424,11 +336,9 @@ DATA: list[dict[str, Any]] = [
{"peer_name": "cvx-red-3", "registrationState": "Registration complete"},
],
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-invalid-role",
"test": VerifyCVXClusterStatus,
(VerifyCVXClusterStatus, "failure-invalid-role"): {
"eos_data": [
{
"enabled": True,
@ -449,56 +359,24 @@ DATA: list[dict[str, Any]] = [
{"peer_name": "cvx-red-3", "registrationState": "Registration complete"},
],
},
"expected": {"result": "failure", "messages": ["CVX Role is not valid: Expected: Master Actual: Standby"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["CVX Role is not valid: Expected: Master Actual: Standby"]},
},
{
"name": "failure-cvx-enabled",
"test": VerifyCVXClusterStatus,
"eos_data": [
{
"enabled": False,
"clusterMode": True,
"clusterStatus": {
"role": "Master",
"peerStatus": {},
},
}
],
"inputs": {
"role": "Master",
"peer_status": [],
},
"expected": {"result": "failure", "messages": ["CVX Server status is not enabled"]},
(VerifyCVXClusterStatus, "failure-cvx-enabled"): {
"eos_data": [{"enabled": False, "clusterMode": True, "clusterStatus": {"role": "Master", "peerStatus": {}}}],
"inputs": {"role": "Master", "peer_status": []},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["CVX Server status is not enabled"]},
},
{
"name": "failure-cluster-enabled",
"test": VerifyCVXClusterStatus,
"eos_data": [
{
"enabled": True,
"clusterMode": False,
"clusterStatus": {},
}
],
"inputs": {
"role": "Master",
"peer_status": [],
},
"expected": {"result": "failure", "messages": ["CVX Server is not a cluster"]},
(VerifyCVXClusterStatus, "failure-cluster-enabled"): {
"eos_data": [{"enabled": True, "clusterMode": False, "clusterStatus": {}}],
"inputs": {"role": "Master", "peer_status": []},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["CVX Server is not a cluster"]},
},
{
"name": "failure-missing-peers",
"test": VerifyCVXClusterStatus,
(VerifyCVXClusterStatus, "failure-missing-peers"): {
"eos_data": [
{
"enabled": True,
"clusterMode": True,
"clusterStatus": {
"role": "Master",
"peerStatus": {
"cvx-red-2": {"peerName": "cvx-red-2", "registrationState": "Registration complete"},
},
},
"clusterStatus": {"role": "Master", "peerStatus": {"cvx-red-2": {"peerName": "cvx-red-2", "registrationState": "Registration complete"}}},
}
],
"inputs": {
@ -508,21 +386,10 @@ DATA: list[dict[str, Any]] = [
{"peer_name": "cvx-red-3", "registrationState": "Registration complete"},
],
},
"expected": {"result": "failure", "messages": ["Unexpected number of peers - Expected: 2 Actual: 1", "cvx-red-3 - Not present"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Unexpected number of peers - Expected: 2 Actual: 1", "cvx-red-3 - Not present"]},
},
{
"name": "failure-invalid-peers",
"test": VerifyCVXClusterStatus,
"eos_data": [
{
"enabled": True,
"clusterMode": True,
"clusterStatus": {
"role": "Master",
"peerStatus": {},
},
}
],
(VerifyCVXClusterStatus, "failure-invalid-peers"): {
"eos_data": [{"enabled": True, "clusterMode": True, "clusterStatus": {"role": "Master", "peerStatus": {}}}],
"inputs": {
"role": "Master",
"peer_status": [
@ -530,11 +397,12 @@ DATA: list[dict[str, Any]] = [
{"peer_name": "cvx-red-3", "registrationState": "Registration complete"},
],
},
"expected": {"result": "failure", "messages": ["Unexpected number of peers - Expected: 2 Actual: 0", "cvx-red-2 - Not present", "cvx-red-3 - Not present"]},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Unexpected number of peers - Expected: 2 Actual: 0", "cvx-red-2 - Not present", "cvx-red-3 - Not present"],
},
},
{
"name": "failure-registration-error",
"test": VerifyCVXClusterStatus,
(VerifyCVXClusterStatus, "failure-registration-error"): {
"eos_data": [
{
"enabled": True,
@ -555,6 +423,9 @@ DATA: list[dict[str, Any]] = [
{"peer_name": "cvx-red-3", "registrationState": "Registration complete"},
],
},
"expected": {"result": "failure", "messages": ["cvx-red-2 - Invalid registration state - Expected: Registration complete Actual: Registration error"]},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["cvx-red-2 - Invalid registration state - Expected: Registration complete Actual: Registration error"],
},
},
]
}

View file

@ -0,0 +1,449 @@
# 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.
"""Tests for anta.tests.evpn.py."""
from __future__ import annotations
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.evpn import VerifyEVPNType5Routes
from tests.units.anta_tests import test
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyEVPNType5Routes, "success-all"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.0.128/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.0.128/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [{"nextHop": "10.100.2.3", "routeType": {"active": True, "valid": True}}],
}
},
},
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.0.128/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.0.128/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [{"nextHop": "10.100.2.3", "routeType": {"active": True, "valid": True}}],
}
},
},
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.4.0/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.4.0/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "10.100.2.3",
"routeType": {"active": True, "valid": True},
"routeDetail": {"extCommunities": ["Route-Target-AS:10:10", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
}
],
}
},
},
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.4.0/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.4.0/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "10.100.2.3",
"routeType": {"active": True, "valid": True},
"routeDetail": {"extCommunities": ["Route-Target-AS:10:10", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
}
],
}
},
},
],
"inputs": {
"prefixes": [
{"address": "10.100.0.128/31", "vni": 10},
{"address": "10.100.0.128/31", "vni": 10, "routes": [{"rd": "10.100.1.3:10", "domain": "local"}]},
{"address": "10.100.4.0/31", "vni": 10, "routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3"}]}]},
{
"address": "10.100.4.1/31",
"vni": 10,
"routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3", "route_targets": ["10:10"]}]}],
},
]
},
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyEVPNType5Routes, "success-ipv6"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.1.0.21",
"asn": 65120,
"evpnRoutes": {
"RD: 10.1.0.21:500 ip-prefix fd00:dc:5::1/128": {
"totalPaths": 1,
"routeKeyDetail": {"ipGenPrefix": "fd00:dc:5::1/128", "domain": "local", "rd": "10.1.0.21:500", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "",
"asPathEntry": {"asPathType": "Local", "asPath": "i"},
"reasonNotBestpath": "noReason",
"routeType": {"active": True, "valid": True},
}
],
},
"RD: 10.1.0.21:500 ip-prefix fd00:dc:5::1/128 remote": {
"totalPaths": 1,
"routeKeyDetail": {"ipGenPrefix": "fd00:dc:5::1/128", "domain": "remote", "rd": "10.1.0.21:500", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "",
"asPathEntry": {"asPathType": "Local", "asPath": "i"},
"reasonNotBestpath": "noReason",
"routeType": {"active": True, "valid": True},
}
],
},
},
}
],
"inputs": {"prefixes": [{"address": "fd00:dc:5::1/128", "vni": 500}]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyEVPNType5Routes, "success-across-all-rds"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.0.128/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.0.128/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [{"nextHop": "10.100.2.3", "routeType": {"active": True, "valid": True}}],
},
"RD: 10.100.1.4:10 ip-prefix 10.100.0.128/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.0.128/31", "domain": "local", "rd": "10.100.1.4:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [{"nextHop": "10.100.2.3", "routeType": {"active": False, "valid": False}}],
},
},
}
],
"inputs": {"prefixes": [{"address": "10.100.0.128/31", "vni": 10}]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyEVPNType5Routes, "success-specific-rd"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.0.128/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.0.128/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{"nextHop": "10.100.2.3", "routeType": {"active": False, "valid": False}},
{"nextHop": "10.100.2.4", "routeType": {"active": True, "valid": True}},
],
}
},
}
],
"inputs": {"prefixes": [{"address": "10.100.0.128/31", "vni": 10, "routes": [{"rd": "10.100.1.3:10", "domain": "local"}]}]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyEVPNType5Routes, "success-specific-nexthop"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.4.0/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.4.0/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "10.100.2.3",
"routeType": {"active": True, "valid": True},
"routeDetail": {"extCommunities": ["Route-Target-AS:10:10", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
},
{
"nextHop": "10.100.2.3",
"routeType": {"active": False, "valid": False},
"routeDetail": {"extCommunities": ["Route-Target-AS:10:10", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
},
],
}
},
}
],
"inputs": {
"prefixes": [{"address": "10.100.4.0/31", "vni": 10, "routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3"}]}]}]
},
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyEVPNType5Routes, "success-RTs"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.4.0/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.4.0/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "10.100.2.3",
"routeType": {"active": True, "valid": True},
"routeDetail": {"extCommunities": ["Route-Target-AS:10:10", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
}
],
}
},
}
],
"inputs": {
"prefixes": [
{
"address": "10.100.4.1/31",
"vni": 10,
"routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3", "route_targets": ["10:10"]}]}],
}
]
},
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyEVPNType5Routes, "failure-all"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.0.128/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.0.128/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [{"nextHop": "10.100.2.3", "routeType": {"active": False, "valid": True}}],
}
},
},
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.0.128/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.0.128/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [{"nextHop": "10.100.2.3", "routeType": {"active": False, "valid": True}}],
}
},
},
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.4.0/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.4.0/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "10.100.2.3",
"routeType": {"active": True, "valid": False},
"routeDetail": {"extCommunities": ["Route-Target-AS:10:10", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
}
],
}
},
},
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.4.0/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.4.0/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "10.100.2.3",
"routeType": {"active": False, "valid": True},
"routeDetail": {"extCommunities": ["Route-Target-AS:10:10", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
}
],
}
},
},
],
"inputs": {
"prefixes": [
{"address": "10.100.0.128/31", "vni": 10},
{"address": "10.100.0.128/31", "vni": 10, "routes": [{"rd": "10.100.1.3:10", "domain": "local"}]},
{"address": "10.100.4.0/31", "vni": 10, "routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3"}]}]},
{
"address": "10.100.4.1/31",
"vni": 10,
"routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3", "route_targets": ["10:10"]}]}],
},
]
},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": [
"Prefix: 10.100.0.128/31 VNI: 10 - No active and valid path found across all RDs",
"Prefix: 10.100.0.128/31 VNI: 10 RD: 10.100.1.3:10 - No active and valid path found",
"Prefix: 10.100.4.0/31 VNI: 10 RD: 10.100.1.3:10 Nexthop: 10.100.2.3 - No active and valid path found",
"Prefix: 10.100.4.1/31 VNI: 10 RD: 10.100.1.3:10 Nexthop: 10.100.2.3 RTs: 10:10 - No active and valid path found",
],
},
},
(VerifyEVPNType5Routes, "failure-not-configured"): {
"eos_data": [
{"vrf": "default", "routerId": "10.100.1.5", "asn": 65102, "evpnRoutes": {}},
{"vrf": "default", "routerId": "10.100.1.5", "asn": 65102, "evpnRoutes": {}},
],
"inputs": {
"prefixes": [
{"address": "10.100.0.128/31", "vni": 10},
{
"address": "10.100.4.1/31",
"vni": 10,
"routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3", "route_targets": ["10:10"]}]}],
},
]
},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Prefix: 10.100.0.128/31 VNI: 10 - No EVPN Type-5 routes found", "Prefix: 10.100.4.1/31 VNI: 10 - No EVPN Type-5 routes found"],
},
},
(VerifyEVPNType5Routes, "failure-route-not-found-with-specified-rd-domain"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.0.128/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.0.128/31", "domain": "remote", "rd": "10.100.1.4:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{"nextHop": "10.100.2.3", "routeType": {"active": False, "valid": False}},
{"nextHop": "10.100.2.4", "routeType": {"active": True, "valid": True}},
],
}
},
}
],
"inputs": {"prefixes": [{"address": "10.100.0.128/31", "vni": 10, "routes": [{"rd": "10.100.1.3:10", "domain": "remote"}]}]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Prefix: 10.100.0.128/31 VNI: 10 RD: 10.100.1.3:10 Domain: remote - Route not found"]},
},
(VerifyEVPNType5Routes, "failiure-specific-nexthop-path-not-found"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.4.0/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.4.0/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "10.100.2.4",
"routeType": {"active": True, "valid": True},
"routeDetail": {"extCommunities": ["Route-Target-AS:10:10", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
}
],
}
},
}
],
"inputs": {
"prefixes": [{"address": "10.100.4.0/31", "vni": 10, "routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3"}]}]}]
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Prefix: 10.100.4.0/31 VNI: 10 RD: 10.100.1.3:10 Nexthop: 10.100.2.3 - Path not found"]},
},
(VerifyEVPNType5Routes, "failiure-specific-nexthop-RTs-path-not-found"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.100.1.5",
"asn": 65102,
"evpnRoutes": {
"RD: 10.100.1.3:10 ip-prefix 10.100.4.0/31": {
"routeKeyDetail": {"ipGenPrefix": "10.100.4.0/31", "domain": "local", "rd": "10.100.1.3:10", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "10.100.2.3",
"routeType": {"active": True, "valid": True},
"routeDetail": {"extCommunities": ["Route-Target-AS:20:20", "TunnelEncap:tunnelTypeVxlan", "EvpnRouterMac:02:1c:73:71:73:45"]},
}
],
}
},
}
],
"inputs": {
"prefixes": [
{
"address": "10.100.4.1/31",
"vni": 10,
"routes": [{"rd": "10.100.1.3:10", "domain": "local", "paths": [{"nexthop": "10.100.2.3", "route_targets": ["10:10"]}]}],
}
]
},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Prefix: 10.100.4.1/31 VNI: 10 RD: 10.100.1.3:10 Nexthop: 10.100.2.3 RTs: 10:10 - Path not found"],
},
},
(VerifyEVPNType5Routes, "failure-ipv6"): {
"eos_data": [
{
"vrf": "default",
"routerId": "10.1.0.21",
"asn": 65120,
"evpnRoutes": {
"RD: 10.1.0.21:500 ip-prefix fd00:dc:5::1/128": {
"totalPaths": 1,
"routeKeyDetail": {"ipGenPrefix": "fd00:dc:5::1/128", "domain": "local", "rd": "10.1.0.21:500", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "",
"asPathEntry": {"asPathType": "Local", "asPath": "i"},
"reasonNotBestpath": "noReason",
"routeType": {"active": True, "valid": False},
}
],
},
"RD: 10.1.0.21:500 ip-prefix fd00:dc:5::1/128 remote": {
"totalPaths": 1,
"routeKeyDetail": {"ipGenPrefix": "fd00:dc:5::1/128", "domain": "remote", "rd": "10.1.0.21:500", "nlriType": "ip-prefix"},
"evpnRoutePaths": [
{
"nextHop": "",
"asPathEntry": {"asPathType": "Local", "asPath": "i"},
"reasonNotBestpath": "noReason",
"routeType": {"active": False, "valid": True},
}
],
},
},
}
],
"inputs": {"prefixes": [{"address": "fd00:dc:5::1/128", "vni": 500}]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Prefix: fd00:dc:5::1/128 VNI: 500 - No active and valid path found across all RDs"]},
},
}

View file

@ -5,15 +5,19 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.field_notices import VerifyFieldNotice44Resolution, VerifyFieldNotice72Resolution
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyFieldNotice44Resolution,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyFieldNotice44Resolution, "success"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
@ -23,343 +27,200 @@ DATA: list[dict[str, Any]] = [
"deviations": [],
"components": [{"name": "Aboot", "version": "Aboot-veos-8.0.0-3255441"}, {"name": "NotAboot", "version": "Aboot-veos-8.0.0-3255441"}],
},
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-4.0",
"test": VerifyFieldNotice44Resolution,
(VerifyFieldNotice44Resolution, "failure-4.0"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
"uptime": 1109144.35,
"modelName": "DCS-7280QRA-C36S",
"details": {
"deviations": [],
"components": [{"name": "Aboot", "version": "Aboot-veos-4.0.1-3255441"}],
},
},
"details": {"deviations": [], "components": [{"name": "Aboot", "version": "Aboot-veos-4.0.1-3255441"}]},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Device is running incorrect version of aboot 4.0.1"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device is running incorrect version of aboot 4.0.1"]},
},
{
"name": "failure-4.1",
"test": VerifyFieldNotice44Resolution,
(VerifyFieldNotice44Resolution, "failure-4.1"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
"uptime": 1109144.35,
"modelName": "DCS-7280QRA-C36S",
"details": {
"deviations": [],
"components": [{"name": "Aboot", "version": "Aboot-veos-4.1.0-3255441"}],
},
},
"details": {"deviations": [], "components": [{"name": "Aboot", "version": "Aboot-veos-4.1.0-3255441"}]},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Device is running incorrect version of aboot 4.1.0"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device is running incorrect version of aboot 4.1.0"]},
},
{
"name": "failure-6.0",
"test": VerifyFieldNotice44Resolution,
(VerifyFieldNotice44Resolution, "failure-6.0"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
"uptime": 1109144.35,
"modelName": "DCS-7280QRA-C36S",
"details": {
"deviations": [],
"components": [{"name": "Aboot", "version": "Aboot-veos-6.0.1-3255441"}],
},
},
"details": {"deviations": [], "components": [{"name": "Aboot", "version": "Aboot-veos-6.0.1-3255441"}]},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Device is running incorrect version of aboot 6.0.1"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device is running incorrect version of aboot 6.0.1"]},
},
{
"name": "failure-6.1",
"test": VerifyFieldNotice44Resolution,
(VerifyFieldNotice44Resolution, "failure-6.1"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
"uptime": 1109144.35,
"modelName": "DCS-7280QRA-C36S",
"details": {
"deviations": [],
"components": [{"name": "Aboot", "version": "Aboot-veos-6.1.1-3255441"}],
},
},
"details": {"deviations": [], "components": [{"name": "Aboot", "version": "Aboot-veos-6.1.1-3255441"}]},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Device is running incorrect version of aboot 6.1.1"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device is running incorrect version of aboot 6.1.1"]},
},
{
"name": "skipped-model",
"test": VerifyFieldNotice44Resolution,
(VerifyFieldNotice44Resolution, "skipped-model"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
"uptime": 1109144.35,
"modelName": "vEOS-lab",
"details": {
"deviations": [],
"components": [{"name": "Aboot", "version": "Aboot-veos-8.0.0-3255441"}],
},
},
"details": {"deviations": [], "components": [{"name": "Aboot", "version": "Aboot-veos-8.0.0-3255441"}]},
}
],
"inputs": None,
"expected": {
"result": "skipped",
"messages": ["Device is not impacted by FN044"],
},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Device is not impacted by FN044"]},
},
{
"name": "failure-no-aboot-component",
"test": VerifyFieldNotice44Resolution,
(VerifyFieldNotice44Resolution, "failure-no-aboot-component"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
"uptime": 1109144.35,
"modelName": "DCS-7280QRA-C36S",
"details": {
"deviations": [],
"components": [{"name": "NotAboot", "version": "Aboot-veos-4.0.1-3255441"}],
},
},
"details": {"deviations": [], "components": [{"name": "NotAboot", "version": "Aboot-veos-4.0.1-3255441"}]},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Aboot component not found"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Aboot component not found"]},
},
{
"name": "success-JPE",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "success-JPE"): {
"eos_data": [
{
"modelName": "DCS-7280SR3-48YC8",
"serialNumber": "JPE2130000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "7"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "7"}]},
}
],
"inputs": None,
"expected": {"result": "success", "messages": ["FN72 is mitigated"]},
"expected": {"result": AntaTestStatus.SUCCESS, "messages": ["FN72 is mitigated"]},
},
{
"name": "success-JAS",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "success-JAS"): {
"eos_data": [
{
"modelName": "DCS-7280SR3-48YC8",
"serialNumber": "JAS2040000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "7"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "7"}]},
}
],
"inputs": None,
"expected": {"result": "success", "messages": ["FN72 is mitigated"]},
"expected": {"result": AntaTestStatus.SUCCESS, "messages": ["FN72 is mitigated"]},
},
{
"name": "success-K-JPE",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "success-K-JPE"): {
"eos_data": [
{
"modelName": "DCS-7280SR3K-48YC8",
"serialNumber": "JPE2133000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "7"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "7"}]},
}
],
"inputs": None,
"expected": {"result": "success", "messages": ["FN72 is mitigated"]},
"expected": {"result": AntaTestStatus.SUCCESS, "messages": ["FN72 is mitigated"]},
},
{
"name": "success-K-JAS",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "success-K-JAS"): {
"eos_data": [
{
"modelName": "DCS-7280SR3K-48YC8",
"serialNumber": "JAS2040000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "7"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "7"}]},
}
],
"inputs": None,
"expected": {"result": "success", "messages": ["FN72 is mitigated"]},
"expected": {"result": AntaTestStatus.SUCCESS, "messages": ["FN72 is mitigated"]},
},
{
"name": "skipped-Serial",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "skipped-Serial"): {
"eos_data": [
{
"modelName": "DCS-7280SR3K-48YC8",
"serialNumber": "BAN2040000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "7"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "7"}]},
}
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["Device not exposed"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Device not exposed"]},
},
{
"name": "skipped-Platform",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "skipped-Platform"): {
"eos_data": [
{
"modelName": "DCS-7150-52-CL",
"serialNumber": "JAS0040000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "5"}],
},
},
{"modelName": "DCS-7150-52-CL", "serialNumber": "JAS0040000", "details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "5"}]}}
],
"inputs": None,
"expected": {
"result": "skipped",
"messages": ["Platform is not impacted by FN072"],
},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Platform is not impacted by FN072"]},
},
{
"name": "skipped-range-JPE",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "skipped-range-JPE"): {
"eos_data": [
{
"modelName": "DCS-7280SR3-48YC8",
"serialNumber": "JPE2131000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "5"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "5"}]},
}
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["Device not exposed"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Device not exposed"]},
},
{
"name": "skipped-range-K-JPE",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "skipped-range-K-JPE"): {
"eos_data": [
{
"modelName": "DCS-7280SR3K-48YC8",
"serialNumber": "JPE2134000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "5"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "5"}]},
}
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["Device not exposed"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Device not exposed"]},
},
{
"name": "skipped-range-JAS",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "skipped-range-JAS"): {
"eos_data": [
{
"modelName": "DCS-7280SR3-48YC8",
"serialNumber": "JAS2041000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "5"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "5"}]},
}
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["Device not exposed"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Device not exposed"]},
},
{
"name": "skipped-range-K-JAS",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "skipped-range-K-JAS"): {
"eos_data": [
{
"modelName": "DCS-7280SR3K-48YC8",
"serialNumber": "JAS2041000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "5"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "5"}]},
}
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["Device not exposed"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Device not exposed"]},
},
{
"name": "failed-JPE",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "failed-JPE"): {
"eos_data": [
{
"modelName": "DCS-7280SR3K-48YC8",
"serialNumber": "JPE2133000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "5"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "5"}]},
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Device is exposed to FN72"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device is exposed to FN72"]},
},
{
"name": "failed-JAS",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "failed-JAS"): {
"eos_data": [
{
"modelName": "DCS-7280SR3-48YC8",
"serialNumber": "JAS2040000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm1", "version": "5"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm1", "version": "5"}]},
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Device is exposed to FN72"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device is exposed to FN72"]},
},
{
"name": "error",
"test": VerifyFieldNotice72Resolution,
(VerifyFieldNotice72Resolution, "error"): {
"eos_data": [
{
"modelName": "DCS-7280SR3-48YC8",
"serialNumber": "JAS2040000",
"details": {
"deviations": [],
"components": [{"name": "FixedSystemvrm2", "version": "5"}],
},
},
"details": {"deviations": [], "components": [{"name": "FixedSystemvrm2", "version": "5"}]},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Error in running test - Component FixedSystemvrm1 not found in 'show version'"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Error in running test - Component FixedSystemvrm1 not found in 'show version"]},
},
]
}

View file

@ -5,15 +5,19 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.flow_tracking import VerifyHardwareFlowTrackerStatus
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyHardwareFlowTrackerStatus,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyHardwareFlowTrackerStatus, "success"): {
"eos_data": [
{
"trackers": {
@ -31,14 +35,12 @@ DATA: list[dict[str, Any]] = [
},
},
"running": True,
},
}
],
"inputs": {"trackers": [{"name": "FLOW-TRACKER"}, {"name": "HARDWARE-TRACKER"}]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-with-optional-field",
"test": VerifyHardwareFlowTrackerStatus,
(VerifyHardwareFlowTrackerStatus, "success-with-optional-field"): {
"eos_data": [
{
"trackers": {
@ -56,7 +58,7 @@ DATA: list[dict[str, Any]] = [
},
},
"running": True,
},
}
],
"inputs": {
"trackers": [
@ -72,21 +74,14 @@ DATA: list[dict[str, Any]] = [
},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-flow-tracking-not-running",
"test": VerifyHardwareFlowTrackerStatus,
(VerifyHardwareFlowTrackerStatus, "failure-flow-tracking-not-running"): {
"eos_data": [{"trackers": {}, "running": False}],
"inputs": {"trackers": [{"name": "FLOW-TRACKER"}]},
"expected": {
"result": "failure",
"messages": ["Hardware flow tracking is not running."],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Hardware flow tracking is not running"]},
},
{
"name": "failure-tracker-not-configured",
"test": VerifyHardwareFlowTrackerStatus,
(VerifyHardwareFlowTrackerStatus, "failure-tracker-not-configured"): {
"eos_data": [
{
"trackers": {
@ -101,14 +96,9 @@ DATA: list[dict[str, Any]] = [
}
],
"inputs": {"trackers": [{"name": "FLOW-Sample"}]},
"expected": {
"result": "failure",
"messages": ["Flow Tracker: FLOW-Sample - Not found"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Flow Tracker: FLOW-Sample - Not found"]},
},
{
"name": "failure-tracker-not-active",
"test": VerifyHardwareFlowTrackerStatus,
(VerifyHardwareFlowTrackerStatus, "failure-tracker-not-active"): {
"eos_data": [
{
"trackers": {
@ -126,7 +116,7 @@ DATA: list[dict[str, Any]] = [
},
},
"running": True,
},
}
],
"inputs": {
"trackers": [
@ -142,14 +132,9 @@ DATA: list[dict[str, Any]] = [
},
]
},
"expected": {
"result": "failure",
"messages": ["Flow Tracker: FLOW-TRACKER - Disabled", "Flow Tracker: HARDWARE-TRACKER - Disabled"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Flow Tracker: FLOW-TRACKER - Disabled", "Flow Tracker: HARDWARE-TRACKER - Disabled"]},
},
{
"name": "failure-incorrect-record-export",
"test": VerifyHardwareFlowTrackerStatus,
(VerifyHardwareFlowTrackerStatus, "failure-incorrect-record-export"): {
"eos_data": [
{
"trackers": {
@ -167,22 +152,16 @@ DATA: list[dict[str, Any]] = [
},
},
"running": True,
},
}
],
"inputs": {
"trackers": [
{
"name": "FLOW-TRACKER",
"record_export": {"on_inactive_timeout": 6000, "on_interval": 30000},
},
{
"name": "HARDWARE-TRACKER",
"record_export": {"on_inactive_timeout": 60000, "on_interval": 300000},
},
{"name": "FLOW-TRACKER", "record_export": {"on_inactive_timeout": 6000, "on_interval": 30000}},
{"name": "HARDWARE-TRACKER", "record_export": {"on_inactive_timeout": 60000, "on_interval": 300000}},
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Flow Tracker: FLOW-TRACKER Inactive Timeout: 6000 Active Interval: 30000 - Incorrect timers - Inactive Timeout: 60000 OnActive Interval: 300000",
"Flow Tracker: HARDWARE-TRACKER Inactive Timeout: 60000 Active Interval: 300000 - Incorrect timers - "
@ -190,9 +169,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-incorrect-exporters",
"test": VerifyHardwareFlowTrackerStatus,
(VerifyHardwareFlowTrackerStatus, "failure-incorrect-exporters"): {
"eos_data": [
{
"trackers": {
@ -216,7 +193,7 @@ DATA: list[dict[str, Any]] = [
},
},
"running": True,
},
}
],
"inputs": {
"trackers": [
@ -237,7 +214,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Flow Tracker: FLOW-TRACKER Exporter: CVP-FLOW - Incorrect local interface - Expected: Loopback10 Actual: Loopback0",
"Flow Tracker: FLOW-TRACKER Exporter: CVP-FLOW - Incorrect template interval - Expected: 3500000 Actual: 3600000",
@ -247,9 +224,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-all-type",
"test": VerifyHardwareFlowTrackerStatus,
(VerifyHardwareFlowTrackerStatus, "failure-all-type"): {
"eos_data": [
{
"trackers": {
@ -291,7 +266,7 @@ DATA: list[dict[str, Any]] = [
},
},
"running": True,
},
}
],
"inputs": {
"trackers": [
@ -301,10 +276,7 @@ DATA: list[dict[str, Any]] = [
"record_export": {"on_inactive_timeout": 60000, "on_interval": 300000},
"exporters": [{"name": "CV-TELEMETRY", "local_interface": "Loopback0", "template_interval": 3600000}],
},
{
"name": "HARDWARE-FLOW",
"record_export": {"on_inactive_timeout": 60000, "on_interval": 300000},
},
{"name": "HARDWARE-FLOW", "record_export": {"on_inactive_timeout": 60000, "on_interval": 300000}},
{
"name": "FLOW-TRACKER2",
"exporters": [
@ -322,7 +294,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Flow Tracker: FLOW-Sample - Not found",
"Flow Tracker: FLOW-TRIGGER - Disabled",
@ -335,4 +307,4 @@ DATA: list[dict[str, Any]] = [
],
},
},
]
}

View file

@ -5,51 +5,45 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.greent import VerifyGreenT, VerifyGreenTCounters
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyGreenTCounters,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyGreenTCounters, "success"): {
"eos_data": [{"sampleRcvd": 0, "sampleDiscarded": 0, "multiDstSampleRcvd": 0, "grePktSent": 1, "sampleSent": 0}],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyGreenTCounters,
(VerifyGreenTCounters, "failure"): {
"eos_data": [{"sampleRcvd": 0, "sampleDiscarded": 0, "multiDstSampleRcvd": 0, "grePktSent": 0, "sampleSent": 0}],
"inputs": None,
"expected": {"result": "failure", "messages": ["GreenT counters are not incremented"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["GreenT counters are not incremented"]},
},
{
"name": "success",
"test": VerifyGreenT,
(VerifyGreenT, "success"): {
"eos_data": [
{
"profiles": {
"default": {"interfaces": [], "appliedInterfaces": [], "samplePolicy": "default", "failures": {}, "appliedInterfaces6": [], "failures6": {}},
"testProfile": {"interfaces": [], "appliedInterfaces": [], "samplePolicy": "default", "failures": {}, "appliedInterfaces6": [], "failures6": {}},
},
},
}
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyGreenT,
(VerifyGreenT, "failure"): {
"eos_data": [
{
"profiles": {
"default": {"interfaces": [], "appliedInterfaces": [], "samplePolicy": "default", "failures": {}, "appliedInterfaces6": [], "failures6": {}},
},
},
"default": {"interfaces": [], "appliedInterfaces": [], "samplePolicy": "default", "failures": {}, "appliedInterfaces6": [], "failures6": {}}
}
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["No GreenT policy is created"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No GreenT policy is created"]},
},
]
}

View file

@ -5,8 +5,11 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.hardware import (
VerifyAdverseDrops,
VerifyEnvironmentCooling,
@ -18,44 +21,41 @@ from anta.tests.hardware import (
)
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyTransceiversManufacturers,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyTransceiversManufacturers, "success"): {
"eos_data": [
{
"xcvrSlots": {
"1": {"mfgName": "Arista Networks", "modelName": "QSFP-100G-DR", "serialNum": "XKT203501340", "hardwareRev": "21"},
"2": {"mfgName": "Arista Networks", "modelName": "QSFP-100G-DR", "serialNum": "XKT203501337", "hardwareRev": "21"},
},
},
}
}
],
"inputs": {"manufacturers": ["Arista Networks"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyTransceiversManufacturers,
(VerifyTransceiversManufacturers, "failure"): {
"eos_data": [
{
"xcvrSlots": {
"1": {"mfgName": "Arista Networks", "modelName": "QSFP-100G-DR", "serialNum": "XKT203501340", "hardwareRev": "21"},
"2": {"mfgName": "Arista Networks", "modelName": "QSFP-100G-DR", "serialNum": "XKT203501337", "hardwareRev": "21"},
},
},
}
}
],
"inputs": {"manufacturers": ["Arista"]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Interface: 1 - Transceiver is from unapproved manufacturers - Expected: Arista Actual: Arista Networks",
"Interface: 2 - Transceiver is from unapproved manufacturers - Expected: Arista Actual: Arista Networks",
],
},
},
{
"name": "success",
"test": VerifyTemperature,
(VerifyTemperature, "success"): {
"eos_data": [
{
"powercycleOnOverheat": "False",
@ -64,14 +64,11 @@ DATA: list[dict[str, Any]] = [
"shutdownOnOverheat": "True",
"systemStatus": "temperatureOk",
"recoveryModeOnOverheat": "recoveryModeNA",
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyTemperature,
(VerifyTemperature, "failure"): {
"eos_data": [
{
"powercycleOnOverheat": "False",
@ -80,14 +77,14 @@ DATA: list[dict[str, Any]] = [
"shutdownOnOverheat": "True",
"systemStatus": "temperatureCritical",
"recoveryModeOnOverheat": "recoveryModeNA",
},
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Device temperature exceeds acceptable limits - Expected: temperatureOk Actual: temperatureCritical"]},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Device temperature exceeds acceptable limits - Expected: temperatureOk Actual: temperatureCritical"],
},
},
{
"name": "success",
"test": VerifyTransceiversTemperature,
(VerifyTransceiversTemperature, "success"): {
"eos_data": [
{
"tempSensors": [
@ -107,17 +104,14 @@ DATA: list[dict[str, Any]] = [
"pidDriverCount": 0,
"isPidDriver": False,
"name": "DomTemperatureSensor54",
},
}
],
"cardSlots": [],
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-hwStatus",
"test": VerifyTransceiversTemperature,
(VerifyTransceiversTemperature, "failure-hwStatus"): {
"eos_data": [
{
"tempSensors": [
@ -137,20 +131,14 @@ DATA: list[dict[str, Any]] = [
"pidDriverCount": 0,
"isPidDriver": False,
"name": "DomTemperatureSensor54",
},
}
],
"cardSlots": [],
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Sensor: DomTemperatureSensor54 - Invalid hardware state - Expected: ok Actual: ko"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Sensor: DomTemperatureSensor54 - Invalid hardware state - Expected: ok Actual: ko"]},
},
{
"name": "failure-alertCount",
"test": VerifyTransceiversTemperature,
(VerifyTransceiversTemperature, "failure-alertCount"): {
"eos_data": [
{
"tempSensors": [
@ -170,20 +158,14 @@ DATA: list[dict[str, Any]] = [
"pidDriverCount": 0,
"isPidDriver": False,
"name": "DomTemperatureSensor54",
},
}
],
"cardSlots": [],
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Sensor: DomTemperatureSensor54 - Incorrect alert counter - Expected: 0 Actual: 1"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Sensor: DomTemperatureSensor54 - Incorrect alert counter - Expected: 0 Actual: 1"]},
},
{
"name": "success",
"test": VerifyEnvironmentSystemCooling,
(VerifyEnvironmentSystemCooling, "success"): {
"eos_data": [
{
"defaultZones": False,
@ -199,14 +181,11 @@ DATA: list[dict[str, Any]] = [
"currentZones": 1,
"configuredZones": 0,
"systemStatus": "coolingOk",
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyEnvironmentSystemCooling,
(VerifyEnvironmentSystemCooling, "failure"): {
"eos_data": [
{
"defaultZones": False,
@ -222,14 +201,11 @@ DATA: list[dict[str, Any]] = [
"currentZones": 1,
"configuredZones": 0,
"systemStatus": "coolingKo",
},
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Device system cooling status invalid - Expected: coolingOk Actual: coolingKo"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device system cooling status invalid - Expected: coolingOk Actual: coolingKo"]},
},
{
"name": "success",
"test": VerifyEnvironmentCooling,
(VerifyEnvironmentCooling, "success"): {
"eos_data": [
{
"defaultZones": False,
@ -253,7 +229,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": True,
"speedStable": True,
"label": "PowerSupply1/1",
},
}
],
"speed": 30,
"label": "PowerSupply1",
@ -271,7 +247,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": True,
"speedStable": True,
"label": "PowerSupply2/1",
},
}
],
"speed": 30,
"label": "PowerSupply2",
@ -291,7 +267,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "1/1",
},
}
],
"speed": 30,
"label": "1",
@ -309,7 +285,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "2/1",
},
}
],
"speed": 30,
"label": "2",
@ -327,7 +303,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "3/1",
},
}
],
"speed": 30,
"label": "3",
@ -345,7 +321,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "4/1",
},
}
],
"speed": 30,
"label": "4",
@ -355,14 +331,12 @@ DATA: list[dict[str, Any]] = [
"currentZones": 1,
"configuredZones": 0,
"systemStatus": "coolingOk",
},
}
],
"inputs": {"states": ["ok"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-additional-states",
"test": VerifyEnvironmentCooling,
(VerifyEnvironmentCooling, "success-additional-states"): {
"eos_data": [
{
"defaultZones": False,
@ -386,7 +360,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": True,
"speedStable": True,
"label": "PowerSupply1/1",
},
}
],
"speed": 30,
"label": "PowerSupply1",
@ -395,7 +369,7 @@ DATA: list[dict[str, Any]] = [
"status": "ok",
"fans": [
{
"status": "Not Inserted",
"status": "powerLoss",
"uptime": 1682498935.9121106,
"maxSpeed": 23000,
"lastSpeedStableChangeTime": 1682499092.4665174,
@ -404,7 +378,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": True,
"speedStable": True,
"label": "PowerSupply2/1",
},
}
],
"speed": 30,
"label": "PowerSupply2",
@ -424,7 +398,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "1/1",
},
}
],
"speed": 30,
"label": "1",
@ -442,7 +416,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "2/1",
},
}
],
"speed": 30,
"label": "2",
@ -460,7 +434,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "3/1",
},
}
],
"speed": 30,
"label": "3",
@ -478,7 +452,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "4/1",
},
}
],
"speed": 30,
"label": "4",
@ -488,14 +462,12 @@ DATA: list[dict[str, Any]] = [
"currentZones": 1,
"configuredZones": 0,
"systemStatus": "coolingOk",
},
}
],
"inputs": {"states": ["ok", "Not Inserted"]},
"expected": {"result": "success"},
"inputs": {"states": ["ok", "powerLoss"]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-fan-tray",
"test": VerifyEnvironmentCooling,
(VerifyEnvironmentCooling, "failure-fan-tray"): {
"eos_data": [
{
"defaultZones": False,
@ -519,7 +491,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": True,
"speedStable": True,
"label": "PowerSupply1/1",
},
}
],
"speed": 30,
"label": "PowerSupply1",
@ -537,7 +509,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": True,
"speedStable": True,
"label": "PowerSupply2/1",
},
}
],
"speed": 30,
"label": "PowerSupply2",
@ -548,7 +520,7 @@ DATA: list[dict[str, Any]] = [
"status": "ok",
"fans": [
{
"status": "down",
"status": "unknownHwStatus",
"uptime": 1682498923.9303148,
"maxSpeed": 17500,
"lastSpeedStableChangeTime": 1682498975.0139885,
@ -557,7 +529,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "1/1",
},
}
],
"speed": 30,
"label": "1",
@ -575,7 +547,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "2/1",
},
}
],
"speed": 30,
"label": "2",
@ -584,7 +556,7 @@ DATA: list[dict[str, Any]] = [
"status": "ok",
"fans": [
{
"status": "Not Inserted",
"status": "powerLoss",
"uptime": 1682498923.9383528,
"maxSpeed": 17500,
"lastSpeedStableChangeTime": 1682498975.0140095,
@ -593,7 +565,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "3/1",
},
}
],
"speed": 30,
"label": "3",
@ -611,7 +583,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "4/1",
},
}
],
"speed": 30,
"label": "4",
@ -621,14 +593,12 @@ DATA: list[dict[str, Any]] = [
"currentZones": 1,
"configuredZones": 0,
"systemStatus": "CoolingKo",
},
}
],
"inputs": {"states": ["ok", "Not Inserted"]},
"expected": {"result": "failure", "messages": ["Fan Tray: 1 Fan: 1/1 - Invalid state - Expected: ok, Not Inserted Actual: down"]},
"inputs": {"states": ["ok", "powerLoss"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Fan Tray: 1 Fan: 1/1 - Invalid state - Expected: ok, powerLoss Actual: unknownHwStatus"]},
},
{
"name": "failure-power-supply",
"test": VerifyEnvironmentCooling,
(VerifyEnvironmentCooling, "failure-power-supply"): {
"eos_data": [
{
"defaultZones": False,
@ -643,7 +613,7 @@ DATA: list[dict[str, Any]] = [
"status": "ok",
"fans": [
{
"status": "down",
"status": "unknownHwStatus",
"uptime": 1682498937.0240965,
"maxSpeed": 23000,
"lastSpeedStableChangeTime": 1682499033.0403435,
@ -652,7 +622,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": True,
"speedStable": True,
"label": "PowerSupply1/1",
},
}
],
"speed": 30,
"label": "PowerSupply1",
@ -670,7 +640,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": True,
"speedStable": True,
"label": "PowerSupply2/1",
},
}
],
"speed": 30,
"label": "PowerSupply2",
@ -690,7 +660,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "1/1",
},
}
],
"speed": 30,
"label": "1",
@ -708,7 +678,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "2/1",
},
}
],
"speed": 30,
"label": "2",
@ -717,7 +687,7 @@ DATA: list[dict[str, Any]] = [
"status": "ok",
"fans": [
{
"status": "Not Inserted",
"status": "powerLoss",
"uptime": 1682498923.9383528,
"maxSpeed": 17500,
"lastSpeedStableChangeTime": 1682498975.0140095,
@ -726,7 +696,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "3/1",
},
}
],
"speed": 30,
"label": "3",
@ -744,7 +714,7 @@ DATA: list[dict[str, Any]] = [
"speedHwOverride": False,
"speedStable": True,
"label": "4/1",
},
}
],
"speed": 30,
"label": "4",
@ -754,19 +724,15 @@ DATA: list[dict[str, Any]] = [
"currentZones": 1,
"configuredZones": 0,
"systemStatus": "CoolingKo",
},
}
],
"inputs": {"states": ["ok", "Not Inserted"]},
"inputs": {"states": ["ok", "powerLoss"]},
"expected": {
"result": "failure",
"messages": [
"Power Slot: PowerSupply1 Fan: PowerSupply1/1 - Invalid state - Expected: ok, Not Inserted Actual: down",
],
"result": AntaTestStatus.FAILURE,
"messages": ["Power Slot: PowerSupply1 Fan: PowerSupply1/1 - Invalid state - Expected: ok, powerLoss Actual: unknownHwStatus"],
},
},
{
"name": "success",
"test": VerifyEnvironmentPower,
(VerifyEnvironmentPower, "success"): {
"eos_data": [
{
"powerSupplies": {
@ -805,62 +771,13 @@ DATA: list[dict[str, Any]] = [
"outputCurrent": 9.828125,
"managed": True,
},
},
},
}
}
],
"inputs": {"states": ["ok"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-additional-states",
"test": VerifyEnvironmentPower,
"eos_data": [
{
"powerSupplies": {
"1": {
"outputPower": 0.0,
"modelName": "PWR-500AC-F",
"capacity": 500.0,
"tempSensors": {
"TempSensorP1/2": {"status": "ok", "temperature": 0.0},
"TempSensorP1/3": {"status": "ok", "temperature": 0.0},
"TempSensorP1/1": {"status": "ok", "temperature": 0.0},
},
"fans": {"FanP1/1": {"status": "ok", "speed": 33}},
"state": "Not Inserted",
"inputCurrent": 0.0,
"dominant": False,
"inputVoltage": 0.0,
"outputCurrent": 0.0,
"managed": True,
},
"2": {
"outputPower": 117.375,
"uptime": 1682498935.9121966,
"modelName": "PWR-500AC-F",
"capacity": 500.0,
"tempSensors": {
"TempSensorP2/1": {"status": "ok", "temperature": 39.0},
"TempSensorP2/3": {"status": "ok", "temperature": 43.0},
"TempSensorP2/2": {"status": "ok", "temperature": 31.0},
},
"fans": {"FanP2/1": {"status": "ok", "speed": 33}},
"state": "ok",
"inputCurrent": 0.572265625,
"dominant": False,
"inputVoltage": 232.5,
"outputCurrent": 9.828125,
"managed": True,
},
},
},
],
"inputs": {"states": ["ok", "Not Inserted"]},
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyEnvironmentPower,
(VerifyEnvironmentPower, "success-additional-states"): {
"eos_data": [
{
"powerSupplies": {
@ -899,24 +816,60 @@ DATA: list[dict[str, Any]] = [
"outputCurrent": 9.828125,
"managed": True,
},
},
},
}
}
],
"inputs": {"states": ["ok", "powerLoss"]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyEnvironmentPower, "failure"): {
"eos_data": [
{
"powerSupplies": {
"1": {
"outputPower": 0.0,
"modelName": "PWR-500AC-F",
"capacity": 500.0,
"tempSensors": {
"TempSensorP1/2": {"status": "ok", "temperature": 0.0},
"TempSensorP1/3": {"status": "ok", "temperature": 0.0},
"TempSensorP1/1": {"status": "ok", "temperature": 0.0},
},
"fans": {"FanP1/1": {"status": "ok", "speed": 33}},
"state": "powerLoss",
"inputCurrent": 0.0,
"dominant": False,
"inputVoltage": 0.0,
"outputCurrent": 0.0,
"managed": True,
},
"2": {
"outputPower": 117.375,
"uptime": 1682498935.9121966,
"modelName": "PWR-500AC-F",
"capacity": 500.0,
"tempSensors": {
"TempSensorP2/1": {"status": "ok", "temperature": 39.0},
"TempSensorP2/3": {"status": "ok", "temperature": 43.0},
"TempSensorP2/2": {"status": "ok", "temperature": 31.0},
},
"fans": {"FanP2/1": {"status": "ok", "speed": 33}},
"state": "ok",
"inputCurrent": 0.572265625,
"dominant": False,
"inputVoltage": 232.5,
"outputCurrent": 9.828125,
"managed": True,
},
}
}
],
"inputs": {"states": ["ok"]},
"expected": {"result": "failure", "messages": ["Power Slot: 1 - Invalid power supplies state - Expected: ok Actual: powerLoss"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Power Slot: 1 - Invalid power supplies state - Expected: ok Actual: powerLoss"]},
},
{
"name": "success",
"test": VerifyAdverseDrops,
"eos_data": [{"totalAdverseDrops": 0}],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyAdverseDrops,
(VerifyAdverseDrops, "success"): {"eos_data": [{"totalAdverseDrops": 0}], "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyAdverseDrops, "failure"): {
"eos_data": [{"totalAdverseDrops": 10}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Incorrect total adverse drops counter - Expected: 0 Actual: 10"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Incorrect total adverse drops counter - Expected: 0 Actual: 10"]},
},
]
}

File diff suppressed because it is too large Load diff

View file

@ -5,24 +5,21 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.lanz import VerifyLANZ
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyLANZ,
"eos_data": [{"lanzEnabled": True}],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyLANZ,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyLANZ, "success"): {"eos_data": [{"lanzEnabled": True}], "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyLANZ, "failure"): {
"eos_data": [{"lanzEnabled": False}],
"inputs": None,
"expected": {"result": "failure", "messages": ["LANZ is not enabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["LANZ is not enabled"]},
},
]
}

View file

@ -5,8 +5,11 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.logging import (
VerifyLoggingAccounting,
VerifyLoggingEntries,
@ -21,312 +24,199 @@ from anta.tests.logging import (
)
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyLoggingPersistent,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyLoggingPersistent, "success"): {
"eos_data": [
"Persistent logging: level debugging\n",
"""Directory of flash:/persist/messages
-rw- 9948 May 10 13:54 messages
33214693376 bytes total (10081136640 bytes free)
""",
"Directory of flash:/persist/messages\n\n -rw- 9948 May 10 13:54 messages\n\n"
" 33214693376 bytes total (10081136640 bytes free)\n\n ",
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-disabled",
"test": VerifyLoggingPersistent,
(VerifyLoggingPersistent, "failure-disabled"): {
"eos_data": [
"Persistent logging: disabled\n",
"""Directory of flash:/persist/messages
-rw- 0 Apr 13 16:29 messages
33214693376 bytes total (10082168832 bytes free)
""",
"Directory of flash:/persist/messages\n\n -rw- 0 Apr 13 16:29 messages\n\n"
" 33214693376 bytes total (10082168832 bytes free)\n\n ",
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Persistent logging is disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Persistent logging is disabled"]},
},
{
"name": "failure-not-saved",
"test": VerifyLoggingPersistent,
(VerifyLoggingPersistent, "failure-not-saved"): {
"eos_data": [
"Persistent logging: level debugging\n",
"""Directory of flash:/persist/messages
-rw- 0 Apr 13 16:29 messages
33214693376 bytes total (10082168832 bytes free)
""",
"Directory of flash:/persist/messages\n\n -rw- 0 Apr 13 16:29 messages\n\n"
" 33214693376 bytes total (10082168832 bytes free)\n\n ",
],
"inputs": None,
"expected": {"result": "failure", "messages": ["No persistent logs are saved in flash"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No persistent logs are saved in flash"]},
},
{
"name": "success",
"test": VerifyLoggingSourceIntf,
(VerifyLoggingSourceIntf, "success"): {
"eos_data": [
"""Trap logging: level informational
Logging source-interface 'Management0', IP Address 172.20.20.12 in VRF MGMT
Logging to '10.22.10.92' port 514 in VRF MGMT via udp
Logging to '10.22.10.93' port 514 in VRF MGMT via tcp
Logging to '10.22.10.94' port 911 in VRF MGMT via udp
""",
"Trap logging: level informational\n Logging source-interface 'Management0', IP Address 172.20.20.12 in VRF MGMT\n"
" Logging to '10.22.10.92' port 514 in VRF MGMT via udp\n Logging to '10.22.10.93' port 514 in VRF MGMT via tcp\n"
" Logging to '10.22.10.94' port 911 in VRF MGMT via udp\n\n "
],
"inputs": {"interface": "Management0", "vrf": "MGMT"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-intf",
"test": VerifyLoggingSourceIntf,
(VerifyLoggingSourceIntf, "failure-intf"): {
"eos_data": [
"""Trap logging: level informational
Logging source-interface 'Management1', IP Address 172.20.20.12 in VRF MGMT
Logging to '10.22.10.92' port 514 in VRF MGMT via udp
Logging to '10.22.10.93' port 514 in VRF MGMT via tcp
Logging to '10.22.10.94' port 911 in VRF MGMT via udp
""",
"Trap logging: level informational\n Logging source-interface 'Management1', IP Address 172.20.20.12 in VRF MGMT\n"
" Logging to '10.22.10.92' port 514 in VRF MGMT via udp\n Logging to '10.22.10.93' port 514 in VRF MGMT via tcp\n"
" Logging to '10.22.10.94' port 911 in VRF MGMT via udp\n\n "
],
"inputs": {"interface": "Management0", "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["Source-interface: Management0 VRF: MGMT - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Source-interface: Management0 VRF: MGMT - Not configured"]},
},
{
"name": "failure-vrf",
"test": VerifyLoggingSourceIntf,
(VerifyLoggingSourceIntf, "failure-vrf"): {
"eos_data": [
"""Trap logging: level informational
Logging source-interface 'Management0', IP Address 172.20.20.12 in VRF default
Logging to '10.22.10.92' port 514 in VRF MGMT via udp
Logging to '10.22.10.93' port 514 in VRF MGMT via tcp
Logging to '10.22.10.94' port 911 in VRF MGMT via udp
""",
"Trap logging: level informational\n Logging source-interface 'Management0', IP Address 172.20.20.12 in VRF default\n"
" Logging to '10.22.10.92' port 514 in VRF MGMT via udp\n Logging to '10.22.10.93' port 514 in VRF MGMT via tcp\n"
" Logging to '10.22.10.94' port 911 in VRF MGMT via udp\n\n "
],
"inputs": {"interface": "Management0", "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["Source-interface: Management0 VRF: MGMT - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Source-interface: Management0 VRF: MGMT - Not configured"]},
},
{
"name": "success",
"test": VerifyLoggingHosts,
(VerifyLoggingHosts, "success"): {
"eos_data": [
"""Trap logging: level informational
Logging source-interface 'Management0', IP Address 172.20.20.12 in VRF MGMT
Logging to '10.22.10.92' port 514 in VRF MGMT via udp
Logging to '10.22.10.93' port 514 in VRF MGMT via tcp
Logging to '10.22.10.94' port 911 in VRF MGMT via udp
""",
"Trap logging: level informational\n Logging source-interface 'Management0', IP Address 172.20.20.12 in VRF MGMT\n"
" Logging to '10.22.10.92' port 514 in VRF MGMT via udp\n Logging to '10.22.10.93' port 514 in VRF MGMT via tcp\n"
" Logging to '10.22.10.94' port 911 in VRF MGMT via udp\n\n "
],
"inputs": {"hosts": ["10.22.10.92", "10.22.10.93", "10.22.10.94"], "vrf": "MGMT"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-hosts",
"test": VerifyLoggingHosts,
(VerifyLoggingHosts, "failure-hosts"): {
"eos_data": [
"""Trap logging: level informational
Logging source-interface 'Management1', IP Address 172.20.20.12 in VRF MGMT
Logging to '10.22.10.92' port 514 in VRF MGMT via udp
Logging to '10.22.10.103' port 514 in VRF MGMT via tcp
Logging to '10.22.10.104' port 911 in VRF MGMT via udp
""",
"Trap logging: level informational\n Logging source-interface 'Management1', IP Address 172.20.20.12 in VRF MGMT\n"
" Logging to '10.22.10.92' port 514 in VRF MGMT via udp\n Logging to '10.22.10.103' port 514 in VRF MGMT via tcp\n"
" Logging to '10.22.10.104' port 911 in VRF MGMT via udp\n\n "
],
"inputs": {"hosts": ["10.22.10.92", "10.22.10.93", "10.22.10.94"], "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["Syslog servers 10.22.10.93, 10.22.10.94 are not configured in VRF MGMT"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Syslog servers 10.22.10.93, 10.22.10.94 are not configured in VRF MGMT"]},
},
{
"name": "failure-vrf",
"test": VerifyLoggingHosts,
(VerifyLoggingHosts, "failure-vrf"): {
"eos_data": [
"""Trap logging: level informational
Logging source-interface 'Management0', IP Address 172.20.20.12 in VRF MGMT
Logging to '10.22.10.92' port 514 in VRF MGMT via udp
Logging to '10.22.10.93' port 514 in VRF default via tcp
Logging to '10.22.10.94' port 911 in VRF default via udp
""",
"Trap logging: level informational\n Logging source-interface 'Management0', IP Address 172.20.20.12 in VRF MGMT\n"
" Logging to '10.22.10.92' port 514 in VRF MGMT via udp\n Logging to '10.22.10.93' port 514 in VRF default via tcp\n"
" Logging to '10.22.10.94' port 911 in VRF default via udp\n\n "
],
"inputs": {"hosts": ["10.22.10.92", "10.22.10.93", "10.22.10.94"], "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["Syslog servers 10.22.10.93, 10.22.10.94 are not configured in VRF MGMT"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Syslog servers 10.22.10.93, 10.22.10.94 are not configured in VRF MGMT"]},
},
{
"name": "success",
"test": VerifyLoggingLogsGeneration,
(VerifyLoggingLogsGeneration, "success"): {
"eos_data": [
"",
"2023-05-10T13:54:21.463497-05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: "
"Message from arista on command-api (10.22.1.107): ANTA VerifyLoggingLogsGeneration validation\n",
"2023-05-10T13:54:21.463497-05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: Message from arista on command-api (10.22.1.107):"
" ANTA VerifyLoggingLogsGeneration validation\n",
],
"inputs": {"severity_level": "informational"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyLoggingLogsGeneration,
(VerifyLoggingLogsGeneration, "failure"): {
"eos_data": ["", "Log Buffer:\n"],
"inputs": {"severity_level": "notifications"},
"expected": {"result": "failure", "messages": ["Logs are not generated"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Logs are not generated"]},
},
{
"name": "success",
"test": VerifyLoggingHostname,
(VerifyLoggingHostname, "success"): {
"eos_data": [
{"hostname": "NW-CORE", "fqdn": "NW-CORE.example.org"},
"",
"2023-05-10T15:41:44.701810-05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: "
"Message from arista on command-api (10.22.1.107): ANTA VerifyLoggingHostname validation\n",
"2023-05-10T15:41:44.701810-05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: Message from arista on command-api (10.22.1.107):"
" ANTA VerifyLoggingHostname validation\n",
],
"inputs": {"severity_level": "informational"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyLoggingHostname,
(VerifyLoggingHostname, "failure"): {
"eos_data": [
{"hostname": "NW-CORE", "fqdn": "NW-CORE.example.org"},
"",
"2023-05-10T13:54:21.463497-05:00 NW-CORE ConfigAgent: %SYS-6-LOGMSG_NOTICE: "
"Message from arista on command-api (10.22.1.107): ANTA VerifyLoggingLogsHostname validation\n",
"2023-05-10T13:54:21.463497-05:00 NW-CORE ConfigAgent: %SYS-6-LOGMSG_NOTICE: Message from arista on command-api (10.22.1.107):"
" ANTA VerifyLoggingLogsHostname validation\n",
],
"inputs": {"severity_level": "notifications"},
"expected": {"result": "failure", "messages": ["Logs are not generated with the device FQDN"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Logs are not generated with the device FQDN"]},
},
{
"name": "success-negative-offset",
"test": VerifyLoggingTimestamp,
(VerifyLoggingTimestamp, "success-negative-offset"): {
"eos_data": [
"",
"2023-05-10T15:41:44.680813-05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: "
"Message from arista on command-api (10.22.1.107): ANTA VerifyLoggingTimestamp validation\n"
"2023-05-10T15:42:44.680813-05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: "
"Other log\n",
"2023-05-10T15:41:44.680813-05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: Message from arista on command-api (10.22.1.107):"
" ANTA VerifyLoggingTimestamp validation\n2023-05-10T15:42:44.680813-05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: Other log\n",
],
"inputs": {"severity_level": "informational"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-positive-offset",
"test": VerifyLoggingTimestamp,
(VerifyLoggingTimestamp, "success-positive-offset"): {
"eos_data": [
"",
"2023-05-10T15:41:44.680813+05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: "
"Message from arista on command-api (10.22.1.107): ANTA VerifyLoggingTimestamp validation\n"
"2023-05-10T15:42:44.680813+05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: "
"Other log\n",
"2023-05-10T15:41:44.680813+05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: Message from arista on command-api (10.22.1.107):"
" ANTA VerifyLoggingTimestamp validation\n2023-05-10T15:42:44.680813+05:00 NW-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_INFO: Other log\n",
],
"inputs": {"severity_level": "informational"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyLoggingTimestamp,
(VerifyLoggingTimestamp, "failure"): {
"eos_data": [
"",
"May 10 13:54:22 NE-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_ALERT: "
"Message from arista on command-api (10.22.1.107): ANTA VerifyLoggingTimestamp validation\n",
"May 10 13:54:22 NE-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_ALERT: Message from arista on command-api (10.22.1.107):"
" ANTA VerifyLoggingTimestamp validation\n",
],
"inputs": {"severity_level": "alerts"},
"expected": {"result": "failure", "messages": ["Logs are not generated with the appropriate timestamp format"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Logs are not generated with the appropriate timestamp format"]},
},
{
"name": "failure-no-matching-log",
"test": VerifyLoggingTimestamp,
"eos_data": [
"",
"May 10 13:54:22 NE-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_NOTICE: Message from arista on command-api (10.22.1.107): BLAH\n",
],
(VerifyLoggingTimestamp, "failure-no-matching-log"): {
"eos_data": ["", "May 10 13:54:22 NE-CORE.example.org ConfigAgent: %SYS-6-LOGMSG_NOTICE: Message from arista on command-api (10.22.1.107): BLAH\n"],
"inputs": {"severity_level": "notifications"},
"expected": {"result": "failure", "messages": ["Logs are not generated with the appropriate timestamp format"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Logs are not generated with the appropriate timestamp format"]},
},
{
"name": "success",
"test": VerifyLoggingAccounting,
(VerifyLoggingAccounting, "success"): {
"eos_data": ["2023 May 10 15:50:31 arista command-api 10.22.1.107 stop service=shell priv-lvl=15 cmd=show aaa accounting logs | tail\n"],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyLoggingAccounting,
(VerifyLoggingAccounting, "failure"): {
"eos_data": ["2023 May 10 15:52:26 arista vty14 10.22.1.107 stop service=shell priv-lvl=15 cmd=show bgp summary\n"],
"inputs": None,
"expected": {"result": "failure", "messages": ["AAA accounting logs are not generated"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["AAA accounting logs are not generated"]},
},
{
"name": "success",
"test": VerifyLoggingErrors,
"eos_data": [""],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyLoggingErrors,
(VerifyLoggingErrors, "success"): {"eos_data": [""], "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyLoggingErrors, "failure"): {
"eos_data": [
"Aug 2 19:57:42 DC1-LEAF1A Mlag: %FWK-3-SOCKET_CLOSE_REMOTE: Connection to Mlag (pid:27200) at tbt://192.168.0.1:4432/+n closed by peer (EOF)",
"Aug 2 19:57:42 DC1-LEAF1A Mlag: %FWK-3-SOCKET_CLOSE_REMOTE: Connection to Mlag (pid:27200) at tbt://192.168.0.1:4432/+n closed by peer (EOF)"
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Device has reported syslog messages with a severity of ERRORS or higher"]},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": [
"Device has reported syslog messages with a severity of ERRORS or higher:\nAug 2 19:57:42 DC1-LEAF1A Mlag:"
" %FWK-3-SOCKET_CLOSE_REMOTE: Connection to Mlag (pid:27200) at tbt://192.168.0.1:4432/+n closed by peer (EOF)"
],
},
},
{
"name": "success",
"test": VerifySyslogLogging,
(VerifySyslogLogging, "success"): {
"eos_data": [
"""Syslog logging: enabled
Buffer logging: level debugging
External configuration:
active:
inactive:
Facility Severity Effective Severity
-------------------- ------------- ------------------
aaa debugging debugging
accounting debugging debugging""",
"Syslog logging: enabled\n Buffer logging: level debugging\n\n External configuration:\n active:\n"
" inactive:\n\n Facility Severity Effective Severity\n"
" -------------------- ------------- ------------------\n aaa debugging"
" debugging\n accounting debugging debugging"
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifySyslogLogging,
(VerifySyslogLogging, "failure"): {
"eos_data": [
"""Syslog logging: disabled
Buffer logging: level debugging
Console logging: level errors
Persistent logging: disabled
Monitor logging: level errors
External configuration:
active:
inactive:
Facility Severity Effective Severity
-------------------- ------------- ------------------
aaa debugging debugging
accounting debugging debugging""",
"Syslog logging: disabled\n Buffer logging: level debugging\n Console logging: level errors\n"
" Persistent logging: disabled\n Monitor logging: level errors\n\n External configuration:\n"
" active:\n inactive:\n\n Facility Severity Effective Severity\n"
" -------------------- ------------- ------------------\n aaa debugging"
" debugging\n accounting debugging debugging"
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Syslog logging is disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Syslog logging is disabled"]},
},
{
"name": "success",
"test": VerifyLoggingEntries,
(VerifyLoggingEntries, "success"): {
"eos_data": [
"""Mar 13 04:10:45 s1-leaf1 ProcMgr: %PROCMGR-6-TERMINATE_RUNNING_PROCESS: Terminating deconfigured/reconfigured process 'SystemInitMonitor' (PID=859)
Mar 13 04:10:45 s1-leaf1 ProcMgr: %PROCMGR-6-PROCESS_TERMINATED: 'SystemInitMonitor' (PID=859, status=9) has terminated.""",
"""Mar 13 04:10:45 s1-leaf1 ProcMgr: %PROCMGR-7-WORKER_WARMSTART_DONE: ProcMgr worker warm start done. (PID=547)""",
"Mar 13 04:10:45 s1-leaf1 ProcMgr: %PROCMGR-6-TERMINATE_RUNNING_PROCESS: Terminating deconfigured/reconfigured process 'SystemInitMonitor'"
" (PID=859)\n Mar 13 04:10:45 s1-leaf1 ProcMgr: %PROCMGR-6-PROCESS_TERMINATED: 'SystemInitMonitor' (PID=859, status=9) has terminated.",
"Mar 13 04:10:45 s1-leaf1 ProcMgr: %PROCMGR-7-WORKER_WARMSTART_DONE: ProcMgr worker warm start done. (PID=547)",
],
"inputs": {
"logging_entries": [
@ -334,17 +224,15 @@ DATA: list[dict[str, Any]] = [
{"regex_match": ".*ProcMgr worker warm start.*", "last_number_messages": 2, "severity_level": "debugging"},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-log-str-not-found",
"test": VerifyLoggingEntries,
(VerifyLoggingEntries, "failure-log-str-not-found"): {
"eos_data": [
"""Mar 12 04:34:01 s1-leaf1 ProcMgr: %PROCMGR-7-WORKER_WARMSTART_DONE: ProcMgr worker warm start done. (PID=559)
Mar 12 04:34:01 s1-leaf1 ProcMgr: %PROCMGR-6-PROCESS_TERMINATED: 'SystemInitMonitor' (PID=867, status=9) has terminated.""",
"""Mar 13 03:58:12 s1-leaf1 ConfigAgent: %SYS-5-CONFIG_SESSION_ABORTED: User cvpsystem aborted
configuration session capiVerify-612-612b34a2ffbf11ef96ba3a348d538ba0 on TerminAttr (localhost)
Mar 13 04:10:45 s1-leaf1 SystemInitMonitor: %SYS-5-SYSTEM_INITIALIZED: System is initialized""",
"Mar 12 04:34:01 s1-leaf1 ProcMgr: %PROCMGR-7-WORKER_WARMSTART_DONE: ProcMgr worker warm start done. (PID=559)\nMar 12 04:34:01 "
"s1-leaf1 ProcMgr: %PROCMGR-6-PROCESS_TERMINATED: 'SystemInitMonitor' (PID=867, status=9) has terminated.",
"Mar 13 03:58:12 s1-leaf1 ConfigAgent: %SYS-5-CONFIG_SESSION_ABORTED: User cvpsystem aborted\n "
"configuration session capiVerify-612-612b34a2ffbf11ef96ba3a348d538ba0 on TerminAttr (localhost)\n "
"Mar 13 04:10:45 s1-leaf1 SystemInitMonitor: %SYS-5-SYSTEM_INITIALIZED: System is initialized",
],
"inputs": {
"logging_entries": [
@ -353,11 +241,11 @@ Mar 12 04:34:01 s1-leaf1 ProcMgr: %PROCMGR-6-PROCESS_TERMINATED: 'SystemInitMoni
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Pattern: .ACCOUNTING-5-EXEC: cvpadmin ssh. - Not found in last 3 informational log entries",
"Pattern: .*ProcMgr worker warm start.* - Not found in last 10 debugging log entries",
"Pattern: `.ACCOUNTING-5-EXEC: cvpadmin ssh.` - Not found in last 3 informational log entries",
"Pattern: `.*ProcMgr worker warm start.*` - Not found in last 10 debugging log entries",
],
},
},
]
}

View file

@ -5,196 +5,103 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.mlag import VerifyMlagConfigSanity, VerifyMlagDualPrimary, VerifyMlagInterfaces, VerifyMlagPrimaryPriority, VerifyMlagReloadDelay, VerifyMlagStatus
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyMlagStatus,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyMlagStatus, "success"): {
"eos_data": [{"state": "active", "negStatus": "connected", "peerLinkStatus": "up", "localIntfStatus": "up"}],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "skipped",
"test": VerifyMlagStatus,
"eos_data": [
{
"state": "disabled",
},
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["MLAG is disabled"]},
(VerifyMlagStatus, "skipped"): {
"eos_data": [{"state": "disabled"}],
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["MLAG is disabled"]},
},
{
"name": "failure-negotiation-status",
"test": VerifyMlagStatus,
(VerifyMlagStatus, "failure-negotiation-status"): {
"eos_data": [{"state": "active", "negStatus": "connecting", "peerLinkStatus": "up", "localIntfStatus": "up"}],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["MLAG negotiation status mismatch - Expected: connected Actual: connecting"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["MLAG negotiation status mismatch - Expected: connected Actual: connecting"]},
},
{
"name": "failure-local-interface",
"test": VerifyMlagStatus,
(VerifyMlagStatus, "failure-local-interface"): {
"eos_data": [{"state": "active", "negStatus": "connected", "peerLinkStatus": "up", "localIntfStatus": "down"}],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Operational state of the MLAG local interface is not correct - Expected: up Actual: down"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Operational state of the MLAG local interface is not correct - Expected: up Actual: down"]},
},
{
"name": "failure-peer-link",
"test": VerifyMlagStatus,
(VerifyMlagStatus, "failure-peer-link"): {
"eos_data": [{"state": "active", "negStatus": "connected", "peerLinkStatus": "down", "localIntfStatus": "up"}],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Operational state of the MLAG peer link is not correct - Expected: up Actual: down"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Operational state of the MLAG peer link is not correct - Expected: up Actual: down"]},
},
{
"name": "success",
"test": VerifyMlagInterfaces,
"eos_data": [
{
"state": "active",
"mlagPorts": {"Disabled": 0, "Configured": 0, "Inactive": 0, "Active-partial": 0, "Active-full": 1},
},
],
"inputs": None,
"expected": {"result": "success"},
(VerifyMlagInterfaces, "success"): {
"eos_data": [{"state": "active", "mlagPorts": {"Disabled": 0, "Configured": 0, "Inactive": 0, "Active-partial": 0, "Active-full": 1}}],
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "skipped",
"test": VerifyMlagInterfaces,
"eos_data": [
{
"state": "disabled",
},
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["MLAG is disabled"]},
(VerifyMlagInterfaces, "skipped"): {
"eos_data": [{"state": "disabled"}],
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["MLAG is disabled"]},
},
{
"name": "failure-active-partial",
"test": VerifyMlagInterfaces,
"eos_data": [
{
"state": "active",
"mlagPorts": {"Disabled": 0, "Configured": 0, "Inactive": 0, "Active-partial": 1, "Active-full": 1},
},
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["MLAG status is not ok - Inactive Ports: 0 Partial Active Ports: 1"],
},
(VerifyMlagInterfaces, "failure-active-partial"): {
"eos_data": [{"state": "active", "mlagPorts": {"Disabled": 0, "Configured": 0, "Inactive": 0, "Active-partial": 1, "Active-full": 1}}],
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["MLAG status is not ok - Inactive Ports: 0 Partial Active Ports: 1"]},
},
{
"name": "failure-inactive",
"test": VerifyMlagInterfaces,
"eos_data": [
{
"state": "active",
"mlagPorts": {"Disabled": 0, "Configured": 0, "Inactive": 1, "Active-partial": 1, "Active-full": 1},
},
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["MLAG status is not ok - Inactive Ports: 1 Partial Active Ports: 1"],
},
(VerifyMlagInterfaces, "failure-inactive"): {
"eos_data": [{"state": "active", "mlagPorts": {"Disabled": 0, "Configured": 0, "Inactive": 1, "Active-partial": 1, "Active-full": 1}}],
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["MLAG status is not ok - Inactive Ports: 1 Partial Active Ports: 1"]},
},
{
"name": "success",
"test": VerifyMlagConfigSanity,
(VerifyMlagConfigSanity, "success"): {
"eos_data": [{"globalConfiguration": {}, "interfaceConfiguration": {}, "mlagActive": True, "mlagConnected": True}],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "skipped",
"test": VerifyMlagConfigSanity,
"eos_data": [
{
"mlagActive": False,
},
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["MLAG is disabled"]},
(VerifyMlagConfigSanity, "skipped"): {
"eos_data": [{"mlagActive": False}],
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["MLAG is disabled"]},
},
{
"name": "failure-global",
"test": VerifyMlagConfigSanity,
(VerifyMlagConfigSanity, "failure-global"): {
"eos_data": [
{
"globalConfiguration": {"mlag": {"globalParameters": {"dual-primary-detection-delay": {"localValue": "0", "peerValue": "200"}}}},
"interfaceConfiguration": {},
"mlagActive": True,
"mlagConnected": True,
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["MLAG config-sanity found in global configuration"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["MLAG config-sanity found in global configuration"]},
},
{
"name": "failure-interface",
"test": VerifyMlagConfigSanity,
(VerifyMlagConfigSanity, "failure-interface"): {
"eos_data": [
{
"globalConfiguration": {},
"interfaceConfiguration": {"trunk-native-vlan mlag30": {"interface": {"Port-Channel30": {"localValue": "123", "peerValue": "3700"}}}},
"mlagActive": True,
"mlagConnected": True,
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["MLAG config-sanity found in interface configuration"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["MLAG config-sanity found in interface configuration"]},
},
{
"name": "success",
"test": VerifyMlagReloadDelay,
(VerifyMlagReloadDelay, "success"): {
"eos_data": [{"state": "active", "reloadDelay": 300, "reloadDelayNonMlag": 330}],
"inputs": {"reload_delay": 300, "reload_delay_non_mlag": 330},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "skipped-disabled",
"test": VerifyMlagReloadDelay,
"eos_data": [
{
"state": "disabled",
},
],
(VerifyMlagReloadDelay, "skipped-disabled"): {
"eos_data": [{"state": "disabled"}],
"inputs": {"reload_delay": 300, "reload_delay_non_mlag": 330},
"expected": {"result": "skipped", "messages": ["MLAG is disabled"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["MLAG is disabled"]},
},
{
"name": "failure",
"test": VerifyMlagReloadDelay,
(VerifyMlagReloadDelay, "failure"): {
"eos_data": [{"state": "active", "reloadDelay": 400, "reloadDelayNonMlag": 430}],
"inputs": {"reload_delay": 300, "reload_delay_non_mlag": 330},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["MLAG reload-delay mismatch - Expected: 300s Actual: 400s", "Delay for non-MLAG ports mismatch - Expected: 330s Actual: 430s"],
},
},
{
"name": "success",
"test": VerifyMlagDualPrimary,
(VerifyMlagDualPrimary, "success"): {
"eos_data": [
{
"state": "active",
@ -203,38 +110,22 @@ DATA: list[dict[str, Any]] = [
"dualPrimaryMlagRecoveryDelay": 60,
"dualPrimaryNonMlagRecoveryDelay": 0,
"detail": {"dualPrimaryDetectionDelay": 200, "dualPrimaryAction": "none"},
},
}
],
"inputs": {"detection_delay": 200, "errdisabled": False, "recovery_delay": 60, "recovery_delay_non_mlag": 0},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "skipped-disabled",
"test": VerifyMlagDualPrimary,
"eos_data": [
{
"state": "disabled",
},
],
(VerifyMlagDualPrimary, "skipped-disabled"): {
"eos_data": [{"state": "disabled"}],
"inputs": {"detection_delay": 200, "errdisabled": False, "recovery_delay": 60, "recovery_delay_non_mlag": 0},
"expected": {"result": "skipped", "messages": ["MLAG is disabled"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["MLAG is disabled"]},
},
{
"name": "failure-disabled",
"test": VerifyMlagDualPrimary,
"eos_data": [
{
"state": "active",
"dualPrimaryDetectionState": "disabled",
"dualPrimaryPortsErrdisabled": False,
},
],
(VerifyMlagDualPrimary, "failure-disabled"): {
"eos_data": [{"state": "active", "dualPrimaryDetectionState": "disabled", "dualPrimaryPortsErrdisabled": False}],
"inputs": {"detection_delay": 200, "errdisabled": False, "recovery_delay": 60, "recovery_delay_non_mlag": 0},
"expected": {"result": "failure", "messages": ["Dual-primary detection is disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Dual-primary detection is disabled"]},
},
{
"name": "failure-wrong-timers",
"test": VerifyMlagDualPrimary,
(VerifyMlagDualPrimary, "failure-wrong-timers"): {
"eos_data": [
{
"state": "active",
@ -243,20 +134,18 @@ DATA: list[dict[str, Any]] = [
"dualPrimaryMlagRecoveryDelay": 160,
"dualPrimaryNonMlagRecoveryDelay": 0,
"detail": {"dualPrimaryDetectionDelay": 300, "dualPrimaryAction": "none"},
},
}
],
"inputs": {"detection_delay": 200, "errdisabled": False, "recovery_delay": 60, "recovery_delay_non_mlag": 0},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Dual-primary detection delay mismatch - Expected: 200 Actual: 300",
"Dual-primary MLAG recovery delay mismatch - Expected: 60 Actual: 160",
],
},
},
{
"name": "failure-wrong-action",
"test": VerifyMlagDualPrimary,
(VerifyMlagDualPrimary, "failure-wrong-action"): {
"eos_data": [
{
"state": "active",
@ -265,17 +154,12 @@ DATA: list[dict[str, Any]] = [
"dualPrimaryMlagRecoveryDelay": 60,
"dualPrimaryNonMlagRecoveryDelay": 0,
"detail": {"dualPrimaryDetectionDelay": 200, "dualPrimaryAction": "none"},
},
}
],
"inputs": {"detection_delay": 200, "errdisabled": True, "recovery_delay": 60, "recovery_delay_non_mlag": 0},
"expected": {
"result": "failure",
"messages": ["Dual-primary action mismatch - Expected: errdisableAllInterfaces Actual: none"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Dual-primary action mismatch - Expected: errdisableAllInterfaces Actual: none"]},
},
{
"name": "failure-wrong-non-mlag-delay",
"test": VerifyMlagDualPrimary,
(VerifyMlagDualPrimary, "failure-wrong-non-mlag-delay"): {
"eos_data": [
{
"state": "active",
@ -284,67 +168,32 @@ DATA: list[dict[str, Any]] = [
"dualPrimaryMlagRecoveryDelay": 60,
"dualPrimaryNonMlagRecoveryDelay": 120,
"detail": {"dualPrimaryDetectionDelay": 200, "dualPrimaryAction": "errdisableAllInterfaces"},
},
}
],
"inputs": {"detection_delay": 200, "errdisabled": True, "recovery_delay": 60, "recovery_delay_non_mlag": 60},
"expected": {
"result": "failure",
"messages": ["Dual-primary non MLAG recovery delay mismatch - Expected: 60 Actual: 120"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Dual-primary non MLAG recovery delay mismatch - Expected: 60 Actual: 120"]},
},
{
"name": "success",
"test": VerifyMlagPrimaryPriority,
"eos_data": [
{
"state": "active",
"detail": {"mlagState": "primary", "primaryPriority": 32767},
}
],
"inputs": {
"primary_priority": 32767,
},
"expected": {"result": "success"},
},
{
"name": "skipped-disabled",
"test": VerifyMlagPrimaryPriority,
"eos_data": [
{
"state": "disabled",
}
],
(VerifyMlagPrimaryPriority, "success"): {
"eos_data": [{"state": "active", "detail": {"mlagState": "primary", "primaryPriority": 32767}}],
"inputs": {"primary_priority": 32767},
"expected": {"result": "skipped", "messages": ["MLAG is disabled"]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-not-primary",
"test": VerifyMlagPrimaryPriority,
"eos_data": [
{
"state": "active",
"detail": {"mlagState": "secondary", "primaryPriority": 32767},
}
],
(VerifyMlagPrimaryPriority, "skipped-disabled"): {
"eos_data": [{"state": "disabled"}],
"inputs": {"primary_priority": 32767},
"expected": {
"result": "failure",
"messages": ["The device is not set as MLAG primary"],
},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["MLAG is disabled"]},
},
{
"name": "failure-incorrect-priority",
"test": VerifyMlagPrimaryPriority,
"eos_data": [
{
"state": "active",
"detail": {"mlagState": "secondary", "primaryPriority": 32767},
}
],
(VerifyMlagPrimaryPriority, "failure-not-primary"): {
"eos_data": [{"state": "active", "detail": {"mlagState": "secondary", "primaryPriority": 32767}}],
"inputs": {"primary_priority": 32767},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["The device is not set as MLAG primary"]},
},
(VerifyMlagPrimaryPriority, "failure-incorrect-priority"): {
"eos_data": [{"state": "active", "detail": {"mlagState": "secondary", "primaryPriority": 32767}}],
"inputs": {"primary_priority": 1},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["The device is not set as MLAG primary", "MLAG primary priority mismatch - Expected: 1 Actual: 32767"],
},
},
]
}

View file

@ -5,15 +5,19 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.multicast import VerifyIGMPSnoopingGlobal, VerifyIGMPSnoopingVlans
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success-enabled",
"test": VerifyIGMPSnoopingVlans,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyIGMPSnoopingVlans, "success-enabled"): {
"eos_data": [
{
"reportFlooding": "disabled",
@ -45,14 +49,12 @@ DATA: list[dict[str, Any]] = [
"robustness": 2,
"immediateLeave": "enabled",
"reportFloodingSwitchPorts": [],
},
}
],
"inputs": {"vlans": {1: True, 42: True}},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-disabled",
"test": VerifyIGMPSnoopingVlans,
(VerifyIGMPSnoopingVlans, "success-disabled"): {
"eos_data": [
{
"reportFlooding": "disabled",
@ -68,19 +70,17 @@ DATA: list[dict[str, Any]] = [
"maxGroups": 65534,
"immediateLeave": "default",
"floodingTraffic": True,
},
}
},
"robustness": 2,
"immediateLeave": "enabled",
"reportFloodingSwitchPorts": [],
},
}
],
"inputs": {"vlans": {42: False}},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-missing-vlan",
"test": VerifyIGMPSnoopingVlans,
(VerifyIGMPSnoopingVlans, "failure-missing-vlan"): {
"eos_data": [
{
"reportFlooding": "disabled",
@ -96,22 +96,20 @@ DATA: list[dict[str, Any]] = [
"maxGroups": 65534,
"immediateLeave": "default",
"floodingTraffic": True,
},
}
},
"robustness": 2,
"immediateLeave": "enabled",
"reportFloodingSwitchPorts": [],
},
}
],
"inputs": {"vlans": {1: False, 42: False}},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["VLAN1 - Incorrect IGMP state - Expected: disabled Actual: enabled", "Supplied vlan 42 is not present on the device"],
},
},
{
"name": "failure-wrong-state",
"test": VerifyIGMPSnoopingVlans,
(VerifyIGMPSnoopingVlans, "failure-wrong-state"): {
"eos_data": [
{
"reportFlooding": "disabled",
@ -127,53 +125,29 @@ DATA: list[dict[str, Any]] = [
"maxGroups": 65534,
"immediateLeave": "default",
"floodingTraffic": True,
},
}
},
"robustness": 2,
"immediateLeave": "enabled",
"reportFloodingSwitchPorts": [],
},
}
],
"inputs": {"vlans": {1: True}},
"expected": {"result": "failure", "messages": ["VLAN1 - Incorrect IGMP state - Expected: enabled Actual: disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VLAN1 - Incorrect IGMP state - Expected: enabled Actual: disabled"]},
},
{
"name": "success-enabled",
"test": VerifyIGMPSnoopingGlobal,
"eos_data": [
{
"reportFlooding": "disabled",
"igmpSnoopingState": "enabled",
"robustness": 2,
"immediateLeave": "enabled",
"reportFloodingSwitchPorts": [],
},
],
(VerifyIGMPSnoopingGlobal, "success-enabled"): {
"eos_data": [{"reportFlooding": "disabled", "igmpSnoopingState": "enabled", "robustness": 2, "immediateLeave": "enabled", "reportFloodingSwitchPorts": []}],
"inputs": {"enabled": True},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-disabled",
"test": VerifyIGMPSnoopingGlobal,
"eos_data": [
{
"reportFlooding": "disabled",
"igmpSnoopingState": "disabled",
},
],
(VerifyIGMPSnoopingGlobal, "success-disabled"): {
"eos_data": [{"reportFlooding": "disabled", "igmpSnoopingState": "disabled"}],
"inputs": {"enabled": False},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-wrong-state",
"test": VerifyIGMPSnoopingGlobal,
"eos_data": [
{
"reportFlooding": "disabled",
"igmpSnoopingState": "disabled",
},
],
(VerifyIGMPSnoopingGlobal, "failure-wrong-state"): {
"eos_data": [{"reportFlooding": "disabled", "igmpSnoopingState": "disabled"}],
"inputs": {"enabled": True},
"expected": {"result": "failure", "messages": ["IGMP state is not valid - Expected: enabled Actual: disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["IGMP state is not valid - Expected: enabled Actual: disabled"]},
},
]
}

View file

@ -5,101 +5,67 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.path_selection import VerifyPathsHealth, VerifySpecificPath
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyPathsHealth,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyPathsHealth, "success"): {
"eos_data": [
{
"dpsPeers": {
"10.255.0.1": {
"dpsGroups": {
"internet": {
"dpsPaths": {
"path3": {"state": "routeResolved", "dpsSessions": {"0": {"active": True}}},
},
},
"mpls": {
"dpsPaths": {
"path4": {"state": "ipsecEstablished", "dpsSessions": {"0": {"active": True}}},
},
},
},
"internet": {"dpsPaths": {"path3": {"state": "routeResolved", "dpsSessions": {"0": {"active": True}}}}},
"mpls": {"dpsPaths": {"path4": {"state": "ipsecEstablished", "dpsSessions": {"0": {"active": True}}}}},
}
},
"10.255.0.2": {
"dpsGroups": {
"internet": {
"dpsPaths": {
"path1": {"state": "ipsecEstablished", "dpsSessions": {"0": {"active": True}}},
},
},
"mpls": {
"dpsPaths": {
"path2": {"state": "routeResolved", "dpsSessions": {"0": {"active": True}}},
},
},
},
"internet": {"dpsPaths": {"path1": {"state": "ipsecEstablished", "dpsSessions": {"0": {"active": True}}}}},
"mpls": {"dpsPaths": {"path2": {"state": "routeResolved", "dpsSessions": {"0": {"active": True}}}}},
}
},
}
},
}
],
"inputs": {},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-peer",
"test": VerifyPathsHealth,
"eos_data": [
{"dpsPeers": {}},
],
(VerifyPathsHealth, "failure-no-peer"): {
"eos_data": [{"dpsPeers": {}}],
"inputs": {},
"expected": {"result": "failure", "messages": ["No path configured for router path-selection"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No path configured for router path-selection"]},
},
{
"name": "failure-not-established",
"test": VerifyPathsHealth,
(VerifyPathsHealth, "failure-not-established"): {
"eos_data": [
{
"dpsPeers": {
"10.255.0.1": {
"dpsGroups": {
"internet": {
"dpsPaths": {
"path3": {"state": "ipsecPending", "dpsSessions": {"0": {"active": False}}},
},
},
"mpls": {
"dpsPaths": {
"path4": {"state": "ipsecPending", "dpsSessions": {"0": {"active": False}}},
},
},
},
"internet": {"dpsPaths": {"path3": {"state": "ipsecPending", "dpsSessions": {"0": {"active": False}}}}},
"mpls": {"dpsPaths": {"path4": {"state": "ipsecPending", "dpsSessions": {"0": {"active": False}}}}},
}
},
"10.255.0.2": {
"dpsGroups": {
"internet": {
"dpsPaths": {
"path1": {"state": "ipsecEstablished", "dpsSessions": {"0": {"active": True}}},
},
},
"mpls": {
"dpsPaths": {
"path2": {"state": "ipsecPending", "dpsSessions": {"0": {"active": False}}},
},
},
},
"internet": {"dpsPaths": {"path1": {"state": "ipsecEstablished", "dpsSessions": {"0": {"active": True}}}}},
"mpls": {"dpsPaths": {"path2": {"state": "ipsecPending", "dpsSessions": {"0": {"active": False}}}}},
}
},
}
},
}
],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 10.255.0.1 Path Group: internet - Invalid path state - Expected: ipsecEstablished, routeResolved Actual: ipsecPending",
"Peer: 10.255.0.1 Path Group: mpls - Invalid path state - Expected: ipsecEstablished, routeResolved Actual: ipsecPending",
@ -107,46 +73,28 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-inactive",
"test": VerifyPathsHealth,
(VerifyPathsHealth, "failure-inactive"): {
"eos_data": [
{
"dpsPeers": {
"10.255.0.1": {
"dpsGroups": {
"internet": {
"dpsPaths": {
"path3": {"state": "routeResolved", "dpsSessions": {"0": {"active": False}}},
},
},
"mpls": {
"dpsPaths": {
"path4": {"state": "routeResolved", "dpsSessions": {"0": {"active": False}}},
},
},
},
"internet": {"dpsPaths": {"path3": {"state": "routeResolved", "dpsSessions": {"0": {"active": False}}}}},
"mpls": {"dpsPaths": {"path4": {"state": "routeResolved", "dpsSessions": {"0": {"active": False}}}}},
}
},
"10.255.0.2": {
"dpsGroups": {
"internet": {
"dpsPaths": {
"path1": {"state": "routeResolved", "dpsSessions": {"0": {"active": True}}},
},
},
"mpls": {
"dpsPaths": {
"path2": {"state": "routeResolved", "dpsSessions": {"0": {"active": False}}},
},
},
},
"internet": {"dpsPaths": {"path1": {"state": "routeResolved", "dpsSessions": {"0": {"active": True}}}}},
"mpls": {"dpsPaths": {"path2": {"state": "routeResolved", "dpsSessions": {"0": {"active": False}}}}},
}
},
}
},
}
],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 10.255.0.1 Path Group internet - Telemetry state inactive",
"Peer: 10.255.0.1 Path Group mpls - Telemetry state inactive",
@ -154,9 +102,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifySpecificPath,
(VerifySpecificPath, "success"): {
"eos_data": [
{
"dpsPeers": {
@ -200,21 +146,10 @@ DATA: list[dict[str, Any]] = [
{"peer": "10.255.0.2", "path_group": "mpls", "source_address": "172.18.13.2", "destination_address": "172.18.15.2"},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-expected-path-group-not-found",
"test": VerifySpecificPath,
"eos_data": [
{
"dpsPeers": {
"10.255.0.2": {
"dpsGroups": {"internet": {}},
},
"10.255.0.1": {"peerName": "", "dpsGroups": {"mpls": {}}},
}
}
],
(VerifySpecificPath, "failure-expected-path-group-not-found"): {
"eos_data": [{"dpsPeers": {"10.255.0.2": {"dpsGroups": {"internet": {}}}, "10.255.0.1": {"peerName": "", "dpsGroups": {"mpls": {}}}}}],
"inputs": {
"paths": [
{"peer": "10.255.0.1", "path_group": "internet", "source_address": "100.64.3.2", "destination_address": "100.64.1.2"},
@ -222,30 +157,27 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 10.255.0.1 PathGroup: internet Source: 100.64.3.2 Destination: 100.64.1.2 - No DPS path found for this peer and path group",
"Peer: 10.255.0.2 PathGroup: mpls Source: 172.18.13.2 Destination: 172.18.15.2 - No DPS path found for this peer and path group",
],
},
},
{
"name": "failure-no-router-path-configured",
"test": VerifySpecificPath,
(VerifySpecificPath, "failure-no-router-path-configured"): {
"eos_data": [{"dpsPeers": {}}],
"inputs": {"paths": [{"peer": "10.255.0.1", "path_group": "internet", "source_address": "100.64.3.2", "destination_address": "100.64.1.2"}]},
"expected": {"result": "failure", "messages": ["Router path-selection not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Router path-selection not configured"]},
},
{
"name": "failure-no-specific-peer-configured",
"test": VerifySpecificPath,
(VerifySpecificPath, "failure-no-specific-peer-configured"): {
"eos_data": [{"dpsPeers": {"10.255.0.2": {}}}],
"inputs": {"paths": [{"peer": "10.255.0.1", "path_group": "internet", "source_address": "172.18.3.2", "destination_address": "172.18.5.2"}]},
"expected": {"result": "failure", "messages": ["Peer: 10.255.0.1 PathGroup: internet Source: 172.18.3.2 Destination: 172.18.5.2 - Peer not found"]},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Peer: 10.255.0.1 PathGroup: internet Source: 172.18.3.2 Destination: 172.18.5.2 - Peer not found"],
},
},
{
"name": "failure-not-established",
"test": VerifySpecificPath,
(VerifySpecificPath, "failure-not-established"): {
"eos_data": [
{
"dpsPeers": {
@ -285,18 +217,16 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 10.255.0.1 PathGroup: internet Source: 172.18.3.2 Destination: 172.18.5.2 - Invalid state path - Expected: ipsecEstablished, routeResolved "
"Actual: ipsecPending",
"Peer: 10.255.0.2 PathGroup: mpls Source: 172.18.13.2 Destination: 172.18.15.2 - Invalid state path - Expected: ipsecEstablished, routeResolved "
"Actual: ipsecPending",
"Peer: 10.255.0.1 PathGroup: internet Source: 172.18.3.2 Destination: 172.18.5.2 - Invalid state path - "
"Expected: ipsecEstablished, routeResolved Actual: ipsecPending",
"Peer: 10.255.0.2 PathGroup: mpls Source: 172.18.13.2 Destination: 172.18.15.2 - Invalid state path - "
"Expected: ipsecEstablished, routeResolved Actual: ipsecPending",
],
},
},
{
"name": "failure-inactive",
"test": VerifySpecificPath,
(VerifySpecificPath, "failure-inactive"): {
"eos_data": [
{
"dpsPeers": {
@ -333,16 +263,14 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 10.255.0.1 PathGroup: internet Source: 172.18.3.2 Destination: 172.18.5.2 - Telemetry state inactive for this path",
"Peer: 10.255.0.2 PathGroup: mpls Source: 172.18.13.2 Destination: 172.18.15.2 - Telemetry state inactive for this path",
],
},
},
{
"name": "failure-source-destination-not-configured",
"test": VerifySpecificPath,
(VerifySpecificPath, "failure-source-destination-not-configured"): {
"eos_data": [
{
"dpsPeers": {
@ -374,11 +302,11 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Peer: 10.255.0.1 PathGroup: internet Source: 172.18.3.2 Destination: 172.18.5.2 - No path matching the source and destination found",
"Peer: 10.255.0.2 PathGroup: mpls Source: 172.18.13.2 Destination: 172.18.15.2 - No path matching the source and destination found",
],
},
},
]
}

View file

@ -5,42 +5,40 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.profiles import VerifyTcamProfile, VerifyUnifiedForwardingTableMode
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyUnifiedForwardingTableMode,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyUnifiedForwardingTableMode, "success"): {
"eos_data": [{"uftMode": "2", "urpfEnabled": False, "chipModel": "bcm56870", "l2TableSize": 163840, "l3TableSize": 147456, "lpmTableSize": 32768}],
"inputs": {"mode": 2},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyUnifiedForwardingTableMode,
(VerifyUnifiedForwardingTableMode, "failure"): {
"eos_data": [{"uftMode": "2", "urpfEnabled": False, "chipModel": "bcm56870", "l2TableSize": 163840, "l3TableSize": 147456, "lpmTableSize": 32768}],
"inputs": {"mode": 3},
"expected": {"result": "failure", "messages": ["Not running the correct UFT mode - Expected: 3 Actual: 2"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Not running the correct UFT mode - Expected: 3 Actual: 2"]},
},
{
"name": "success",
"test": VerifyTcamProfile,
(VerifyTcamProfile, "success"): {
"eos_data": [
{"pmfProfiles": {"FixedSystem": {"config": "test", "configType": "System Profile", "status": "test", "mode": "tcam"}}, "lastProgrammingStatus": {}},
{"pmfProfiles": {"FixedSystem": {"config": "test", "configType": "System Profile", "status": "test", "mode": "tcam"}}, "lastProgrammingStatus": {}}
],
"inputs": {"profile": "test"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyTcamProfile,
(VerifyTcamProfile, "failure"): {
"eos_data": [
{"pmfProfiles": {"FixedSystem": {"config": "test", "configType": "System Profile", "status": "default", "mode": "tcam"}}, "lastProgrammingStatus": {}},
{"pmfProfiles": {"FixedSystem": {"config": "test", "configType": "System Profile", "status": "default", "mode": "tcam"}}, "lastProgrammingStatus": {}}
],
"inputs": {"profile": "test"},
"expected": {"result": "failure", "messages": ["Incorrect profile running on device: default"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Incorrect profile running on device: default"]},
},
]
}

View file

@ -5,15 +5,19 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.ptp import VerifyPtpGMStatus, VerifyPtpLockStatus, VerifyPtpModeStatus, VerifyPtpOffset, VerifyPtpPortModeStatus
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyPtpModeStatus,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyPtpModeStatus, "success"): {
"eos_data": [
{
"ptpMode": "ptpBoundaryClock",
@ -31,26 +35,17 @@ DATA: list[dict[str, Any]] = [
"ptpIntfSummaries": {},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyPtpModeStatus,
(VerifyPtpModeStatus, "failure"): {
"eos_data": [{"ptpMode": "ptpDisabled", "ptpIntfSummaries": {}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Not configured as a PTP Boundary Clock - Actual: ptpDisabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Not configured as a PTP Boundary Clock - Actual: ptpDisabled"]},
},
{
"name": "skipped",
"test": VerifyPtpModeStatus,
(VerifyPtpModeStatus, "skipped"): {
"eos_data": [{"ptpIntfSummaries": {}}],
"inputs": None,
"expected": {"result": "skipped", "messages": ["PTP is not configured"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["PTP is not configured"]},
},
{
"name": "success",
"test": VerifyPtpGMStatus,
(VerifyPtpGMStatus, "success"): {
"eos_data": [
{
"ptpMode": "ptpBoundaryClock",
@ -72,11 +67,9 @@ DATA: list[dict[str, Any]] = [
}
],
"inputs": {"gmid": "0xec:46:70:ff:fe:00:ff:a8"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyPtpGMStatus,
(VerifyPtpGMStatus, "failure"): {
"eos_data": [
{
"ptpMode": "ptpBoundaryClock",
@ -97,22 +90,16 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {"gmid": "0xec:46:70:ff:fe:00:ff:a8"},
"expected": {
"result": "failure",
"messages": [
"The device is locked to the incorrect Grandmaster - Expected: 0xec:46:70:ff:fe:00:ff:a8 Actual: 0x00:1c:73:ff:ff:0a:00:01",
],
"result": AntaTestStatus.FAILURE,
"messages": ["The device is locked to the incorrect Grandmaster - Expected: 0xec:46:70:ff:fe:00:ff:a8 Actual: 0x00:1c:73:ff:ff:0a:00:01"],
},
},
{
"name": "skipped",
"test": VerifyPtpGMStatus,
(VerifyPtpGMStatus, "skipped"): {
"eos_data": [{"ptpIntfSummaries": {}}],
"inputs": {"gmid": "0xec:46:70:ff:fe:00:ff:a8"},
"expected": {"result": "skipped", "messages": ["PTP is not configured"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["PTP is not configured"]},
},
{
"name": "success",
"test": VerifyPtpLockStatus,
(VerifyPtpLockStatus, "success"): {
"eos_data": [
{
"ptpMode": "ptpBoundaryClock",
@ -133,12 +120,9 @@ DATA: list[dict[str, Any]] = [
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyPtpLockStatus,
(VerifyPtpLockStatus, "failure"): {
"eos_data": [
{
"ptpMode": "ptpBoundaryClock",
@ -157,24 +141,13 @@ DATA: list[dict[str, Any]] = [
},
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Lock is more than 60s old - Actual: 157s"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Lock is more than 60s old - Actual: 157s"]},
},
{
"name": "skipped",
"test": VerifyPtpLockStatus,
(VerifyPtpLockStatus, "skipped"): {
"eos_data": [{"ptpIntfSummaries": {}}],
"inputs": None,
"expected": {
"result": "skipped",
"messages": [
"PTP is not configured",
],
},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["PTP is not configured"]},
},
{
"name": "success",
"test": VerifyPtpOffset,
(VerifyPtpOffset, "success"): {
"eos_data": [
{
"monitorEnabled": True,
@ -201,12 +174,9 @@ DATA: list[dict[str, Any]] = [
],
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyPtpOffset,
(VerifyPtpOffset, "failure"): {
"eos_data": [
{
"monitorEnabled": True,
@ -233,29 +203,16 @@ DATA: list[dict[str, Any]] = [
],
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": [
"Interface: Ethernet27/1 - Timing offset from master is greater than +/- 1000ns: Actual: 1200, -1300",
],
"result": AntaTestStatus.FAILURE,
"messages": ["Interface: Ethernet27/1 - Timing offset from master is greater than +/- 1000ns: Actual: 1200, -1300"],
},
},
{
"name": "skipped",
"test": VerifyPtpOffset,
"eos_data": [
{
"monitorEnabled": True,
"ptpMonitorData": [],
},
],
"inputs": None,
"expected": {"result": "skipped", "messages": ["PTP is not configured"]},
(VerifyPtpOffset, "skipped"): {
"eos_data": [{"monitorEnabled": True, "ptpMonitorData": []}],
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["PTP is not configured"]},
},
{
"name": "success",
"test": VerifyPtpPortModeStatus,
(VerifyPtpPortModeStatus, "success"): {
"eos_data": [
{
"ptpMode": "ptpBoundaryClock",
@ -293,19 +250,13 @@ DATA: list[dict[str, Any]] = [
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-interfaces",
"test": VerifyPtpPortModeStatus,
(VerifyPtpPortModeStatus, "failure-no-interfaces"): {
"eos_data": [{"ptpIntfSummaries": {}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["No interfaces are PTP enabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No interfaces are PTP enabled"]},
},
{
"name": "failure-invalid-state",
"test": VerifyPtpPortModeStatus,
(VerifyPtpPortModeStatus, "failure-invalid-state"): {
"eos_data": [
{
"ptpMode": "ptpBoundaryClock",
@ -336,7 +287,6 @@ DATA: list[dict[str, Any]] = [
},
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["The following interface(s) are not in a valid PTP state: Ethernet53, Ethernet1"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["The following interface(s) are not in a valid PTP state: Ethernet53, Ethernet1"]},
},
]
}

File diff suppressed because it is too large Load diff

View file

@ -5,65 +5,58 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.services import VerifyDNSLookup, VerifyDNSServers, VerifyErrdisableRecovery, VerifyHostname
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyHostname,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyHostname, "success"): {
"eos_data": [{"hostname": "s1-spine1", "fqdn": "s1-spine1.fun.aristanetworks.com"}],
"inputs": {"hostname": "s1-spine1"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-incorrect-hostname",
"test": VerifyHostname,
(VerifyHostname, "failure-incorrect-hostname"): {
"eos_data": [{"hostname": "s1-spine2", "fqdn": "s1-spine1.fun.aristanetworks.com"}],
"inputs": {"hostname": "s1-spine1"},
"expected": {
"result": "failure",
"messages": ["Incorrect Hostname - Expected: s1-spine1 Actual: s1-spine2"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Incorrect Hostname - Expected: s1-spine1 Actual: s1-spine2"]},
},
{
"name": "success",
"test": VerifyDNSLookup,
(VerifyDNSLookup, "success"): {
"eos_data": [
{
"messages": [
"Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\nName:\tarista.com\nAddress: 151.101.130.132\nName:\tarista.com\n"
"Address: 151.101.2.132\nName:\tarista.com\nAddress: 151.101.194.132\nName:\tarista.com\nAddress: 151.101.66.132\n\n"
"Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\nName:\tarista.com\nAddress: 151.101.130.132\n"
"Name:\tarista.com\nAddress: 151.101.2.132\nName:\tarista.com\nAddress: 151.101.194.132\nName:\tarista.com\nAddress: 151.101.66.132\n\n"
]
},
{"messages": ["Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\nName:\twww.google.com\nAddress: 172.217.12.100\n\n"]},
],
"inputs": {"domain_names": ["arista.com", "www.google.com"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyDNSLookup,
(VerifyDNSLookup, "failure"): {
"eos_data": [
{"messages": ["Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\n*** Can't find arista.ca: No answer\n\n"]},
{"messages": ["Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\nName:\twww.google.com\nAddress: 172.217.12.100\n\n"]},
{"messages": ["Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\n*** Can't find google.ca: No answer\n\n"]},
],
"inputs": {"domain_names": ["arista.ca", "www.google.com", "google.ca"]},
"expected": {"result": "failure", "messages": ["The following domain(s) are not resolved to an IP address: arista.ca, google.ca"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["The following domain(s) are not resolved to an IP address: arista.ca, google.ca"]},
},
{
"name": "success",
"test": VerifyDNSServers,
(VerifyDNSServers, "success"): {
"eos_data": [
{
"nameServerConfigs": [
{"ipAddr": "10.14.0.1", "vrf": "default", "priority": 0},
{"ipAddr": "10.14.0.11", "vrf": "MGMT", "priority": 1},
{"ipAddr": "fd12:3456:789a::1", "vrf": "default", "priority": 0},
],
]
}
],
"inputs": {
@ -73,32 +66,20 @@ DATA: list[dict[str, Any]] = [
{"server_address": "fd12:3456:789a::1", "vrf": "default", "priority": 0},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-dns-found",
"test": VerifyDNSServers,
"eos_data": [
{
"nameServerConfigs": [],
}
],
(VerifyDNSServers, "failure-no-dns-found"): {
"eos_data": [{"nameServerConfigs": []}],
"inputs": {
"dns_servers": [{"server_address": "10.14.0.10", "vrf": "default", "priority": 0}, {"server_address": "10.14.0.21", "vrf": "MGMT", "priority": 1}]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Server 10.14.0.10 VRF: default Priority: 0 - Not configured", "Server 10.14.0.21 VRF: MGMT Priority: 1 - Not configured"],
},
},
{
"name": "failure-incorrect-dns-details",
"test": VerifyDNSServers,
"eos_data": [
{
"nameServerConfigs": [{"ipAddr": "10.14.0.1", "vrf": "CS", "priority": 1}, {"ipAddr": "10.14.0.11", "vrf": "MGMT", "priority": 1}],
}
],
(VerifyDNSServers, "failure-incorrect-dns-details"): {
"eos_data": [{"nameServerConfigs": [{"ipAddr": "10.14.0.1", "vrf": "CS", "priority": 1}, {"ipAddr": "10.14.0.11", "vrf": "MGMT", "priority": 1}]}],
"inputs": {
"dns_servers": [
{"server_address": "10.14.0.1", "vrf": "CS", "priority": 0},
@ -107,7 +88,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Server 10.14.0.1 VRF: CS Priority: 0 - Incorrect priority - Priority: 1",
"Server 10.14.0.11 VRF: default Priority: 0 - Not configured",
@ -115,94 +96,62 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyErrdisableRecovery,
(VerifyErrdisableRecovery, "success"): {
"eos_data": [
# Adding empty line on purpose to verify they are skipped
"""
Errdisable Reason Timer Status Timer Interval
------------------------------ ----------------- --------------
acl Enabled 300
bpduguard Enabled 300
arp-inspection Enabled 30
"""
"\n Errdisable Reason Timer Status Timer Interval\n ------------------------------"
" ----------------- --------------\n acl Enabled 300\n\n "
" bpduguard Enabled 300\n arp-inspection "
" Enabled 30\n "
],
"inputs": {"reasons": [{"reason": "acl", "interval": 300}, {"reason": "bpduguard", "interval": 300}]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-reason-missing",
"test": VerifyErrdisableRecovery,
(VerifyErrdisableRecovery, "failure-reason-missing"): {
"eos_data": [
"""
Errdisable Reason Timer Status Timer Interval
------------------------------ ----------------- --------------
acl Enabled 300
bpduguard Enabled 300
arp-inspection Enabled 30
"""
"\n Errdisable Reason Timer Status Timer Interval\n ------------------------------"
" ----------------- --------------\n acl Enabled 300\n "
" bpduguard Enabled 300\n arp-inspection Enabled"
" 30\n "
],
"inputs": {"reasons": [{"reason": "acl", "interval": 300}, {"reason": "arp-inspection", "interval": 30}, {"reason": "tapagg", "interval": 30}]},
"expected": {
"result": "failure",
"messages": ["Reason: tapagg Status: Enabled Interval: 30 - Not found"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Reason: tapagg Status: Enabled Interval: 30 - Not found"]},
},
{
"name": "failure-reason-disabled",
"test": VerifyErrdisableRecovery,
(VerifyErrdisableRecovery, "failure-reason-disabled"): {
"eos_data": [
"""
Errdisable Reason Timer Status Timer Interval
------------------------------ ----------------- --------------
acl Disabled 300
bpduguard Enabled 300
arp-inspection Enabled 30
"""
"\n Errdisable Reason Timer Status Timer Interval\n ------------------------------ "
"----------------- --------------\n acl Disabled 300\n "
"bpduguard Enabled 300\n arp-inspection Enabled"
" 30\n "
],
"inputs": {"reasons": [{"reason": "acl", "interval": 300}, {"reason": "arp-inspection", "interval": 30}]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Reason: acl Status: Enabled Interval: 300 - Incorrect configuration - Status: Disabled Interval: 300"],
},
},
{
"name": "failure-interval-not-ok",
"test": VerifyErrdisableRecovery,
(VerifyErrdisableRecovery, "failure-interval-not-ok"): {
"eos_data": [
"""
Errdisable Reason Timer Status Timer Interval
------------------------------ ----------------- --------------
acl Enabled 300
bpduguard Enabled 300
arp-inspection Enabled 30
"""
"\n Errdisable Reason Timer Status Timer Interval\n ------------------------------ "
"----------------- --------------\n acl Enabled 300\n "
"bpduguard Enabled 300\n arp-inspection Enabled "
" 30\n "
],
"inputs": {"reasons": [{"reason": "acl", "interval": 30}, {"reason": "arp-inspection", "interval": 30}]},
"expected": {
"result": "failure",
"messages": [
"Reason: acl Status: Enabled Interval: 30 - Incorrect configuration - Status: Enabled Interval: 300",
],
"result": AntaTestStatus.FAILURE,
"messages": ["Reason: acl Status: Enabled Interval: 30 - Incorrect configuration - Status: Enabled Interval: 300"],
},
},
{
"name": "failure-all-type",
"test": VerifyErrdisableRecovery,
(VerifyErrdisableRecovery, "failure-all-type"): {
"eos_data": [
"""
Errdisable Reason Timer Status Timer Interval
------------------------------ ----------------- --------------
acl Disabled 300
bpduguard Enabled 300
arp-inspection Enabled 30
"""
"\n Errdisable Reason Timer Status Timer Interval\n ------------------------------ "
"----------------- --------------\n acl Disabled 300\n "
"bpduguard Enabled 300\n arp-inspection Enabled "
" 30\n "
],
"inputs": {"reasons": [{"reason": "acl", "interval": 30}, {"reason": "arp-inspection", "interval": 300}, {"reason": "tapagg", "interval": 30}]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Reason: acl Status: Enabled Interval: 30 - Incorrect configuration - Status: Disabled Interval: 300",
"Reason: arp-inspection Status: Enabled Interval: 300 - Incorrect configuration - Status: Enabled Interval: 30",
@ -210,4 +159,4 @@ DATA: list[dict[str, Any]] = [
],
},
},
]
}

View file

@ -5,8 +5,11 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.snmp import (
VerifySnmpContact,
VerifySnmpErrorCounters,
@ -23,234 +26,117 @@ from anta.tests.snmp import (
)
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifySnmpStatus,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifySnmpStatus, "success"): {
"eos_data": [{"vrfs": {"snmpVrfs": ["MGMT", "default"]}, "enabled": True}],
"inputs": {"vrf": "MGMT"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-wrong-vrf",
"test": VerifySnmpStatus,
(VerifySnmpStatus, "failure-wrong-vrf"): {
"eos_data": [{"vrfs": {"snmpVrfs": ["default"]}, "enabled": True}],
"inputs": {"vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["VRF: MGMT - SNMP agent disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: MGMT - SNMP agent disabled"]},
},
{
"name": "failure-disabled",
"test": VerifySnmpStatus,
(VerifySnmpStatus, "failure-disabled"): {
"eos_data": [{"vrfs": {"snmpVrfs": ["default"]}, "enabled": False}],
"inputs": {"vrf": "default"},
"expected": {"result": "failure", "messages": ["VRF: default - SNMP agent disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: default - SNMP agent disabled"]},
},
{
"name": "success",
"test": VerifySnmpIPv4Acl,
(VerifySnmpIPv4Acl, "success"): {
"eos_data": [{"ipAclList": {"aclList": [{"type": "Ip4Acl", "name": "ACL_IPV4_SNMP", "configuredVrfs": ["MGMT"], "activeVrfs": ["MGMT"]}]}}],
"inputs": {"number": 1, "vrf": "MGMT"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-wrong-number",
"test": VerifySnmpIPv4Acl,
(VerifySnmpIPv4Acl, "failure-wrong-number"): {
"eos_data": [{"ipAclList": {"aclList": []}}],
"inputs": {"number": 1, "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["VRF: MGMT - Incorrect SNMP IPv4 ACL(s) - Expected: 1 Actual: 0"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: MGMT - Incorrect SNMP IPv4 ACL(s) - Expected: 1 Actual: 0"]},
},
{
"name": "failure-wrong-vrf",
"test": VerifySnmpIPv4Acl,
(VerifySnmpIPv4Acl, "failure-wrong-vrf"): {
"eos_data": [{"ipAclList": {"aclList": [{"type": "Ip4Acl", "name": "ACL_IPV4_SNMP", "configuredVrfs": ["default"], "activeVrfs": ["default"]}]}}],
"inputs": {"number": 1, "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["VRF: MGMT - Following SNMP IPv4 ACL(s) not configured or active: ACL_IPV4_SNMP"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: MGMT - Following SNMP IPv4 ACL(s) not configured or active: ACL_IPV4_SNMP"]},
},
{
"name": "success",
"test": VerifySnmpIPv6Acl,
(VerifySnmpIPv6Acl, "success"): {
"eos_data": [{"ipv6AclList": {"aclList": [{"type": "Ip6Acl", "name": "ACL_IPV6_SNMP", "configuredVrfs": ["MGMT"], "activeVrfs": ["MGMT"]}]}}],
"inputs": {"number": 1, "vrf": "MGMT"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-wrong-number",
"test": VerifySnmpIPv6Acl,
(VerifySnmpIPv6Acl, "failure-wrong-number"): {
"eos_data": [{"ipv6AclList": {"aclList": []}}],
"inputs": {"number": 1, "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["VRF: MGMT - Incorrect SNMP IPv6 ACL(s) - Expected: 1 Actual: 0"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: MGMT - Incorrect SNMP IPv6 ACL(s) - Expected: 1 Actual: 0"]},
},
{
"name": "failure-wrong-vrf",
"test": VerifySnmpIPv6Acl,
(VerifySnmpIPv6Acl, "failure-wrong-vrf"): {
"eos_data": [{"ipv6AclList": {"aclList": [{"type": "Ip6Acl", "name": "ACL_IPV6_SNMP", "configuredVrfs": ["default"], "activeVrfs": ["default"]}]}}],
"inputs": {"number": 1, "vrf": "MGMT"},
"expected": {"result": "failure", "messages": ["VRF: MGMT - Following SNMP IPv6 ACL(s) not configured or active: ACL_IPV6_SNMP"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VRF: MGMT - Following SNMP IPv6 ACL(s) not configured or active: ACL_IPV6_SNMP"]},
},
{
"name": "success",
"test": VerifySnmpLocation,
"eos_data": [
{
"location": {"location": "New York"},
}
],
(VerifySnmpLocation, "success"): {
"eos_data": [{"location": {"location": "New York"}}],
"inputs": {"location": "New York"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-incorrect-location",
"test": VerifySnmpLocation,
"eos_data": [
{
"location": {"location": "Europe"},
}
],
(VerifySnmpLocation, "failure-incorrect-location"): {
"eos_data": [{"location": {"location": "Europe"}}],
"inputs": {"location": "New York"},
"expected": {
"result": "failure",
"messages": ["Incorrect SNMP location - Expected: New York Actual: Europe"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Incorrect SNMP location - Expected: New York Actual: Europe"]},
},
{
"name": "failure-details-not-configured",
"test": VerifySnmpLocation,
"eos_data": [
{
"location": {"location": ""},
}
],
(VerifySnmpLocation, "failure-details-not-configured"): {
"eos_data": [{"location": {"location": ""}}],
"inputs": {"location": "New York"},
"expected": {
"result": "failure",
"messages": ["SNMP location is not configured"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["SNMP location is not configured"]},
},
{
"name": "success",
"test": VerifySnmpContact,
"eos_data": [
{
"contact": {"contact": "Jon@example.com"},
}
],
(VerifySnmpContact, "success"): {
"eos_data": [{"contact": {"contact": "Jon@example.com"}}],
"inputs": {"contact": "Jon@example.com"},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-incorrect-contact",
"test": VerifySnmpContact,
"eos_data": [
{
"contact": {"contact": "Jon@example.com"},
}
],
(VerifySnmpContact, "failure-incorrect-contact"): {
"eos_data": [{"contact": {"contact": "Jon@example.com"}}],
"inputs": {"contact": "Bob@example.com"},
"expected": {
"result": "failure",
"messages": ["Incorrect SNMP contact - Expected: Bob@example.com Actual: Jon@example.com"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Incorrect SNMP contact - Expected: Bob@example.com Actual: Jon@example.com"]},
},
{
"name": "failure-details-not-configured",
"test": VerifySnmpContact,
"eos_data": [
{
"contact": {"contact": ""},
}
],
(VerifySnmpContact, "failure-details-not-configured"): {
"eos_data": [{"contact": {"contact": ""}}],
"inputs": {"contact": "Bob@example.com"},
"expected": {
"result": "failure",
"messages": ["SNMP contact is not configured"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["SNMP contact is not configured"]},
},
{
"name": "success",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {
"inGetPdus": 3,
"inGetNextPdus": 2,
"inSetPdus": 3,
"outGetResponsePdus": 3,
"outTrapPdus": 9,
},
}
],
(VerifySnmpPDUCounters, "success"): {
"eos_data": [{"counters": {"inGetPdus": 3, "inGetNextPdus": 2, "inSetPdus": 3, "outGetResponsePdus": 3, "outTrapPdus": 9}}],
"inputs": {},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-specific-pdus",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {
"inGetPdus": 3,
"inGetNextPdus": 0,
"inSetPdus": 0,
"outGetResponsePdus": 0,
"outTrapPdus": 9,
},
}
],
(VerifySnmpPDUCounters, "success-specific-pdus"): {
"eos_data": [{"counters": {"inGetPdus": 3, "inGetNextPdus": 0, "inSetPdus": 0, "outGetResponsePdus": 0, "outTrapPdus": 9}}],
"inputs": {"pdus": ["inGetPdus", "outTrapPdus"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-counters-not-found",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {},
}
],
(VerifySnmpPDUCounters, "failure-counters-not-found"): {
"eos_data": [{"counters": {}}],
"inputs": {},
"expected": {"result": "failure", "messages": ["SNMP counters not found"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["SNMP counters not found"]},
},
{
"name": "failure-incorrect-counters",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {
"inGetPdus": 0,
"inGetNextPdus": 2,
"inSetPdus": 0,
"outGetResponsePdus": 3,
"outTrapPdus": 9,
},
}
],
(VerifySnmpPDUCounters, "failure-incorrect-counters"): {
"eos_data": [{"counters": {"inGetPdus": 0, "inGetNextPdus": 2, "inSetPdus": 0, "outGetResponsePdus": 3, "outTrapPdus": 9}}],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["The following SNMP PDU counters are not found or have zero PDU counters: inGetPdus, inSetPdus"],
},
},
{
"name": "failure-pdu-not-found",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {
"inGetNextPdus": 0,
"inSetPdus": 0,
"outGetResponsePdus": 0,
},
}
],
(VerifySnmpPDUCounters, "failure-pdu-not-found"): {
"eos_data": [{"counters": {"inGetNextPdus": 0, "inSetPdus": 0, "outGetResponsePdus": 0}}],
"inputs": {"pdus": ["inGetPdus", "outTrapPdus"]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["The following SNMP PDU counters are not found or have zero PDU counters: inGetPdus, outTrapPdus"],
},
},
{
"name": "success",
"test": VerifySnmpErrorCounters,
(VerifySnmpErrorCounters, "success"): {
"eos_data": [
{
"counters": {
@ -262,15 +148,13 @@ DATA: list[dict[str, Any]] = [
"outNoSuchNameErrs": 0,
"outBadValueErrs": 0,
"outGeneralErrs": 0,
},
}
}
],
"inputs": {},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-specific-counters",
"test": VerifySnmpErrorCounters,
(VerifySnmpErrorCounters, "success-specific-counters"): {
"eos_data": [
{
"counters": {
@ -282,26 +166,18 @@ DATA: list[dict[str, Any]] = [
"outNoSuchNameErrs": 0,
"outBadValueErrs": 10,
"outGeneralErrs": 1,
},
}
}
],
"inputs": {"error_counters": ["inVersionErrs", "inParseErrs"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-counters-not-found",
"test": VerifySnmpErrorCounters,
"eos_data": [
{
"counters": {},
}
],
(VerifySnmpErrorCounters, "failure-counters-not-found"): {
"eos_data": [{"counters": {}}],
"inputs": {},
"expected": {"result": "failure", "messages": ["SNMP counters not found"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["SNMP counters not found"]},
},
{
"name": "failure-incorrect-counters",
"test": VerifySnmpErrorCounters,
(VerifySnmpErrorCounters, "failure-incorrect-counters"): {
"eos_data": [
{
"counters": {
@ -313,18 +189,16 @@ DATA: list[dict[str, Any]] = [
"outNoSuchNameErrs": 0,
"outBadValueErrs": 2,
"outGeneralErrs": 0,
},
}
}
],
"inputs": {},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["The following SNMP error counters are not found or have non-zero error counters: inParseErrs, inVersionErrs, outBadValueErrs"],
},
},
{
"name": "success",
"test": VerifySnmpHostLogging,
(VerifySnmpHostLogging, "success"): {
"eos_data": [
{
"logging": {
@ -344,61 +218,38 @@ DATA: list[dict[str, Any]] = [
{"hostname": "snmp-server-01", "vrf": "default"},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-logging-disabled",
"test": VerifySnmpHostLogging,
(VerifySnmpHostLogging, "failure-logging-disabled"): {
"eos_data": [{"logging": {"loggingEnabled": False}}],
"inputs": {"hosts": [{"hostname": "192.168.1.100", "vrf": "default"}, {"hostname": "192.168.1.101", "vrf": "MGMT"}]},
"expected": {"result": "failure", "messages": ["SNMP logging is disabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["SNMP logging is disabled"]},
},
{
"name": "failure-mismatch-vrf",
"test": VerifySnmpHostLogging,
(VerifySnmpHostLogging, "failure-mismatch-vrf"): {
"eos_data": [{"logging": {"loggingEnabled": True, "hosts": {"192.168.1.100": {"port": 162, "vrf": "MGMT"}, "192.168.1.101": {"port": 162, "vrf": "Test"}}}}],
"inputs": {"hosts": [{"hostname": "192.168.1.100", "vrf": "default"}, {"hostname": "192.168.1.101", "vrf": "MGMT"}]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Host: 192.168.1.100 VRF: default - Incorrect VRF - Actual: MGMT", "Host: 192.168.1.101 VRF: MGMT - Incorrect VRF - Actual: Test"],
},
},
{
"name": "failure-host-not-configured",
"test": VerifySnmpHostLogging,
(VerifySnmpHostLogging, "failure-host-not-configured"): {
"eos_data": [{"logging": {"loggingEnabled": True, "hosts": {"192.168.1.100": {"port": 162, "vrf": "MGMT"}, "192.168.1.103": {"port": 162, "vrf": "Test"}}}}],
"inputs": {"hosts": [{"hostname": "192.168.1.101", "vrf": "default"}, {"hostname": "192.168.1.102", "vrf": "MGMT"}]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Host: 192.168.1.101 VRF: default - Not configured", "Host: 192.168.1.102 VRF: MGMT - Not configured"],
},
},
{
"name": "success",
"test": VerifySnmpUser,
(VerifySnmpUser, "success"): {
"eos_data": [
{
"usersByVersion": {
"v1": {
"users": {
"Test1": {
"groupName": "TestGroup1",
},
}
},
"v2c": {
"users": {
"Test2": {
"groupName": "TestGroup2",
},
}
},
"v1": {"users": {"Test1": {"groupName": "TestGroup1"}}},
"v2c": {"users": {"Test2": {"groupName": "TestGroup2"}}},
"v3": {
"users": {
"Test3": {
"groupName": "TestGroup3",
"v3Params": {"authType": "SHA-384", "privType": "AES-128"},
},
"Test3": {"groupName": "TestGroup3", "v3Params": {"authType": "SHA-384", "privType": "AES-128"}},
"Test4": {"groupName": "TestGroup3", "v3Params": {"authType": "SHA-512", "privType": "AES-192"}},
}
},
@ -413,25 +264,10 @@ DATA: list[dict[str, Any]] = [
{"username": "Test4", "group_name": "TestGroup3", "version": "v3", "auth_type": "SHA-512", "priv_type": "AES-192"},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-not-configured",
"test": VerifySnmpUser,
"eos_data": [
{
"usersByVersion": {
"v3": {
"users": {
"Test3": {
"groupName": "TestGroup3",
"v3Params": {"authType": "SHA-384", "privType": "AES-128"},
},
}
},
}
}
],
(VerifySnmpUser, "failure-not-configured"): {
"eos_data": [{"usersByVersion": {"v3": {"users": {"Test3": {"groupName": "TestGroup3", "v3Params": {"authType": "SHA-384", "privType": "AES-128"}}}}}}],
"inputs": {
"snmp_users": [
{"username": "Test1", "group_name": "TestGroup1", "version": "v1"},
@ -441,7 +277,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"User: Test1 Group: TestGroup1 Version: v1 - Not found",
"User: Test2 Group: TestGroup2 Version: v2c - Not found",
@ -449,70 +285,30 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-incorrect-group",
"test": VerifySnmpUser,
(VerifySnmpUser, "failure-incorrect-group"): {
"eos_data": [
{
"usersByVersion": {
"v1": {
"users": {
"Test1": {
"groupName": "TestGroup2",
},
}
},
"v2c": {
"users": {
"Test2": {
"groupName": "TestGroup1",
},
}
},
"v3": {},
}
}
{"usersByVersion": {"v1": {"users": {"Test1": {"groupName": "TestGroup2"}}}, "v2c": {"users": {"Test2": {"groupName": "TestGroup1"}}}, "v3": {}}}
],
"inputs": {
"snmp_users": [
{"username": "Test1", "group_name": "TestGroup1", "version": "v1"},
{"username": "Test2", "group_name": "TestGroup2", "version": "v2c"},
]
"snmp_users": [{"username": "Test1", "group_name": "TestGroup1", "version": "v1"}, {"username": "Test2", "group_name": "TestGroup2", "version": "v2c"}]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"User: Test1 Group: TestGroup1 Version: v1 - Incorrect user group - Actual: TestGroup2",
"User: Test2 Group: TestGroup2 Version: v2c - Incorrect user group - Actual: TestGroup1",
],
},
},
{
"name": "failure-incorrect-auth-encryption",
"test": VerifySnmpUser,
(VerifySnmpUser, "failure-incorrect-auth-encryption"): {
"eos_data": [
{
"usersByVersion": {
"v1": {
"users": {
"Test1": {
"groupName": "TestGroup1",
},
}
},
"v2c": {
"users": {
"Test2": {
"groupName": "TestGroup2",
},
}
},
"v1": {"users": {"Test1": {"groupName": "TestGroup1"}}},
"v2c": {"users": {"Test2": {"groupName": "TestGroup2"}}},
"v3": {
"users": {
"Test3": {
"groupName": "TestGroup3",
"v3Params": {"authType": "SHA-512", "privType": "AES-192"},
},
"Test3": {"groupName": "TestGroup3", "v3Params": {"authType": "SHA-512", "privType": "AES-192"}},
"Test4": {"groupName": "TestGroup4", "v3Params": {"authType": "SHA-384", "privType": "AES-128"}},
}
},
@ -528,7 +324,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"User: Test3 Group: TestGroup3 Version: v3 - Incorrect authentication type - Expected: SHA-384 Actual: SHA-512",
"User: Test3 Group: TestGroup3 Version: v3 - Incorrect privacy type - Expected: AES-128 Actual: AES-192",
@ -537,9 +333,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifySnmpNotificationHost,
(VerifySnmpNotificationHost, "success"): {
"eos_data": [
{
"hosts": [
@ -568,11 +362,9 @@ DATA: list[dict[str, Any]] = [
{"hostname": "192.168.1.101", "vrf": "MGMT", "notification_type": "trap", "version": "v2c", "udp_port": 162, "community_string": "public"},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-not-configured",
"test": VerifySnmpNotificationHost,
(VerifySnmpNotificationHost, "failure-not-configured"): {
"eos_data": [{"hosts": []}],
"inputs": {
"notification_hosts": [
@ -580,11 +372,9 @@ DATA: list[dict[str, Any]] = [
{"hostname": "192.168.1.101", "vrf": "default", "notification_type": "trap", "version": "v2c", "udp_port": 162, "community_string": "public"},
]
},
"expected": {"result": "failure", "messages": ["No SNMP host is configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No SNMP host is configured"]},
},
{
"name": "failure-details-host-not-found",
"test": VerifySnmpNotificationHost,
(VerifySnmpNotificationHost, "failure-details-host-not-found"): {
"eos_data": [
{
"hosts": [
@ -595,7 +385,7 @@ DATA: list[dict[str, Any]] = [
"notificationType": "trap",
"protocolVersion": "v3",
"v3Params": {"user": "public", "securityLevel": "authNoPriv"},
},
}
]
}
],
@ -605,11 +395,9 @@ DATA: list[dict[str, Any]] = [
{"hostname": "192.168.1.101", "vrf": "default", "notification_type": "trap", "version": "v2c", "udp_port": 162, "community_string": "public"},
]
},
"expected": {"result": "failure", "messages": ["Host: 192.168.1.101 VRF: default Version: v2c - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Host: 192.168.1.101 VRF: default Version: v2c - Not configured"]},
},
{
"name": "failure-incorrect-notification-type",
"test": VerifySnmpNotificationHost,
(VerifySnmpNotificationHost, "failure-incorrect-notification-type"): {
"eos_data": [
{
"hosts": [
@ -639,16 +427,14 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Host: 192.168.1.100 VRF: default - Incorrect notification type - Expected: inform Actual: trap",
"Host: 192.168.1.101 VRF: default - Incorrect notification type - Expected: trap Actual: inform",
],
},
},
{
"name": "failure-incorrect-udp-port",
"test": VerifySnmpNotificationHost,
(VerifySnmpNotificationHost, "failure-incorrect-udp-port"): {
"eos_data": [
{
"hosts": [
@ -678,16 +464,14 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Host: 192.168.1.100 VRF: default - Incorrect UDP port - Expected: 162 Actual: 163",
"Host: 192.168.1.101 VRF: default - Incorrect UDP port - Expected: 162 Actual: 164",
],
},
},
{
"name": "failure-incorrect-community-string-version-v1-v2c",
"test": VerifySnmpNotificationHost,
(VerifySnmpNotificationHost, "failure-incorrect-community-string-version-v1-v2c"): {
"eos_data": [
{
"hosts": [
@ -717,16 +501,14 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Host: 192.168.1.100 VRF: default Version: v1 - Incorrect community string - Expected: public Actual: private",
"Host: 192.168.1.101 VRF: default Version: v2c - Incorrect community string - Expected: public Actual: private",
],
},
},
{
"name": "failure-incorrect-user-for-version-v3",
"test": VerifySnmpNotificationHost,
(VerifySnmpNotificationHost, "failure-incorrect-user-for-version-v3"): {
"eos_data": [
{
"hosts": [
@ -742,58 +524,35 @@ DATA: list[dict[str, Any]] = [
}
],
"inputs": {
"notification_hosts": [
{"hostname": "192.168.1.100", "vrf": "default", "notification_type": "trap", "version": "v3", "udp_port": 162, "user": "public"},
]
"notification_hosts": [{"hostname": "192.168.1.100", "vrf": "default", "notification_type": "trap", "version": "v3", "udp_port": 162, "user": "public"}]
},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Host: 192.168.1.100 VRF: default Version: v3 - Incorrect user - Expected: public Actual: private"],
},
"expected": {"result": "failure", "messages": ["Host: 192.168.1.100 VRF: default Version: v3 - Incorrect user - Expected: public Actual: private"]},
},
{
"name": "success",
"test": VerifySnmpSourceInterface,
"eos_data": [
{
"srcIntf": {"sourceInterfaces": {"default": "Ethernet1", "MGMT": "Management0"}},
}
],
(VerifySnmpSourceInterface, "success"): {
"eos_data": [{"srcIntf": {"sourceInterfaces": {"default": "Ethernet1", "MGMT": "Management0"}}}],
"inputs": {"interfaces": [{"interface": "Ethernet1", "vrf": "default"}, {"interface": "Management0", "vrf": "MGMT"}]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-not-configured",
"test": VerifySnmpSourceInterface,
"eos_data": [
{
"srcIntf": {},
}
],
(VerifySnmpSourceInterface, "failure-not-configured"): {
"eos_data": [{"srcIntf": {}}],
"inputs": {"interfaces": [{"interface": "Ethernet1", "vrf": "default"}, {"interface": "Management0", "vrf": "MGMT"}]},
"expected": {"result": "failure", "messages": ["SNMP source interface(s) not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["SNMP source interface(s) not configured"]},
},
{
"name": "failure-incorrect-interfaces",
"test": VerifySnmpSourceInterface,
"eos_data": [
{
"srcIntf": {
"sourceInterfaces": {
"default": "Management0",
}
},
}
],
(VerifySnmpSourceInterface, "failure-incorrect-interfaces"): {
"eos_data": [{"srcIntf": {"sourceInterfaces": {"default": "Management0"}}}],
"inputs": {"interfaces": [{"interface": "Ethernet1", "vrf": "default"}, {"interface": "Management0", "vrf": "MGMT"}]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Source Interface: Ethernet1 VRF: default - Incorrect source interface - Actual: Management0",
"Source Interface: Management0 VRF: MGMT - Not configured",
],
},
},
{
"name": "success",
"test": VerifySnmpGroup,
(VerifySnmpGroup, "success"): {
"eos_data": [
{
"groups": {
@ -853,11 +612,9 @@ DATA: list[dict[str, Any]] = [
},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-incorrect-view",
"test": VerifySnmpGroup,
(VerifySnmpGroup, "failure-incorrect-view"): {
"eos_data": [
{
"groups": {
@ -918,7 +675,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Group: Group1 Version: v1 - Incorrect Read view - Expected: group_read_1 Actual: group_read",
"Group: Group1 Version: v1 - Incorrect Write view - Expected: group_write_1 Actual: group_write",
@ -931,9 +688,7 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-view-config-not-found",
"test": VerifySnmpGroup,
(VerifySnmpGroup, "failure-view-config-not-found"): {
"eos_data": [
{
"groups": {
@ -983,17 +738,11 @@ DATA: list[dict[str, Any]] = [
"snmp_groups": [
{"group_name": "Group1", "version": "v1", "read_view": "group_read", "write_view": "group_write", "notify_view": "group_notify"},
{"group_name": "Group2", "version": "v2c", "read_view": "group_read", "write_view": "group_write", "notify_view": "group_notify"},
{
"group_name": "Group3",
"version": "v3",
"write_view": "group_write",
"notify_view": "group_notify",
"authentication": "priv",
},
{"group_name": "Group3", "version": "v3", "write_view": "group_write", "notify_view": "group_notify", "authentication": "priv"},
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Group: Group1 Version: v1 Read View: group_read - Not configured",
"Group: Group1 Version: v1 Write View: group_write - Not configured",
@ -1006,18 +755,8 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-group-version-not-configured",
"test": VerifySnmpGroup,
"eos_data": [
{
"groups": {
"Group1": {"versions": {"v1": {}}},
"Group2": {"versions": {"v2c": {}}},
"Group3": {"versions": {"v3": {}}},
}
}
],
(VerifySnmpGroup, "failure-group-version-not-configured"): {
"eos_data": [{"groups": {"Group1": {"versions": {"v1": {}}}, "Group2": {"versions": {"v2c": {}}}, "Group3": {"versions": {"v3": {}}}}}],
"inputs": {
"snmp_groups": [
{"group_name": "Group1", "version": "v1", "read_view": "group_read_1", "write_view": "group_write_1"},
@ -1033,17 +772,11 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"messages": [
"Group: Group1 Version: v1 - Not configured",
"Group: Group2 Version: v2c - Not configured",
"Group: Group3 Version: v3 - Not configured",
],
"result": AntaTestStatus.FAILURE,
"messages": ["Group: Group1 Version: v1 - Not configured", "Group: Group2 Version: v2c - Not configured", "Group: Group3 Version: v3 - Not configured"],
},
},
{
"name": "failure-incorrect-v3-auth-model",
"test": VerifySnmpGroup,
(VerifySnmpGroup, "failure-incorrect-v3-auth-model"): {
"eos_data": [
{
"groups": {
@ -1059,7 +792,7 @@ DATA: list[dict[str, Any]] = [
"notifyViewConfig": True,
}
}
},
}
}
}
],
@ -1072,42 +805,20 @@ DATA: list[dict[str, Any]] = [
"write_view": "group_write",
"notify_view": "group_notify",
"authentication": "priv",
},
}
]
},
"expected": {
"result": "failure",
"messages": [
"Group: Group3 Version: v3 - Incorrect security model - Expected: v3Priv Actual: v3Auth",
],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Group: Group3 Version: v3 - Incorrect security model - Expected: v3Priv Actual: v3Auth"]},
},
{
"name": "failure-view-not-configured",
"test": VerifySnmpGroup,
(VerifySnmpGroup, "failure-view-not-configured"): {
"eos_data": [
{
"groups": {
"Group3": {"versions": {"v3": {"secModel": "v3NoAuth", "readView": "group_read", "readViewConfig": True, "writeView": "", "notifyView": ""}}},
"Group3": {"versions": {"v3": {"secModel": "v3NoAuth", "readView": "group_read", "readViewConfig": True, "writeView": "", "notifyView": ""}}}
}
}
],
"inputs": {
"snmp_groups": [
{
"group_name": "Group3",
"version": "v3",
"read_view": "group_read",
"write_view": "group_write",
"authentication": "noauth",
},
]
},
"expected": {
"result": "failure",
"messages": [
"Group: Group3 Version: v3 View: write - Not configured",
],
},
"inputs": {"snmp_groups": [{"group_name": "Group3", "version": "v3", "read_view": "group_read", "write_view": "group_write", "authentication": "noauth"}]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Group: Group3 Version: v3 View: write - Not configured"]},
},
]
}

View file

@ -5,41 +5,29 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.software import VerifyEOSExtensions, VerifyEOSVersion, VerifyTerminAttrVersion
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyEOSVersion,
"eos_data": [
{
"modelName": "vEOS-lab",
"internalVersion": "4.27.0F-24305004.4270F",
"version": "4.27.0F",
},
],
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyEOSVersion, "success"): {
"eos_data": [{"modelName": "vEOS-lab", "internalVersion": "4.27.0F-24305004.4270F", "version": "4.27.0F"}],
"inputs": {"versions": ["4.27.0F", "4.28.0F"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyEOSVersion,
"eos_data": [
{
"modelName": "vEOS-lab",
"internalVersion": "4.27.0F-24305004.4270F",
"version": "4.27.0F",
},
],
(VerifyEOSVersion, "failure"): {
"eos_data": [{"modelName": "vEOS-lab", "internalVersion": "4.27.0F-24305004.4270F", "version": "4.27.0F"}],
"inputs": {"versions": ["4.27.1F"]},
"expected": {"result": "failure", "messages": ["EOS version mismatch - Actual: 4.27.0F not in Expected: 4.27.1F"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["EOS version mismatch - Actual: 4.27.0F not in Expected: 4.27.1F"]},
},
{
"name": "success",
"test": VerifyTerminAttrVersion,
(VerifyTerminAttrVersion, "success"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
@ -49,18 +37,14 @@ DATA: list[dict[str, Any]] = [
"deviations": [],
"components": [{"name": "Aboot", "version": "Aboot-veos-8.0.0-3255441"}],
"switchType": "fixedSystem",
"packages": {
"TerminAttr-core": {"release": "1", "version": "v1.17.0"},
},
"packages": {"TerminAttr-core": {"release": "1", "version": "v1.17.0"}},
},
},
}
],
"inputs": {"versions": ["v1.17.0", "v1.18.1"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyTerminAttrVersion,
(VerifyTerminAttrVersion, "failure"): {
"eos_data": [
{
"imageFormatVersion": "1.0",
@ -70,28 +54,18 @@ DATA: list[dict[str, Any]] = [
"deviations": [],
"components": [{"name": "Aboot", "version": "Aboot-veos-8.0.0-3255441"}],
"switchType": "fixedSystem",
"packages": {
"TerminAttr-core": {"release": "1", "version": "v1.17.0"},
},
"packages": {"TerminAttr-core": {"release": "1", "version": "v1.17.0"}},
},
},
}
],
"inputs": {"versions": ["v1.17.1", "v1.18.1"]},
"expected": {"result": "failure", "messages": ["TerminAttr version mismatch - Actual: v1.17.0 not in Expected: v1.17.1, v1.18.1"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["TerminAttr version mismatch - Actual: v1.17.0 not in Expected: v1.17.1, v1.18.1"]},
},
{
"name": "success-no-extensions",
"test": VerifyEOSExtensions,
"eos_data": [
{"extensions": {}, "extensionStoredDir": "flash:", "warnings": ["No extensions are available"]},
{"extensions": []},
],
"inputs": None,
"expected": {"result": "success"},
(VerifyEOSExtensions, "success-no-extensions"): {
"eos_data": [{"extensions": {}, "extensionStoredDir": "flash:", "warnings": ["No extensions are available"]}, {"extensions": []}],
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-extensions",
"test": VerifyEOSExtensions,
(VerifyEOSExtensions, "success-extensions"): {
"eos_data": [
{
"extensions": {
@ -110,17 +84,14 @@ DATA: list[dict[str, Any]] = [
"description": "An extension for Arista Cloud Connect gateway",
"affectedAgents": [],
"agentsToRestart": [],
},
}
}
},
{"extensions": ["AristaCloudGateway-1.0.1-1.swix"]},
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyEOSExtensions,
(VerifyEOSExtensions, "failure"): {
"eos_data": [
{
"extensions": {
@ -139,17 +110,14 @@ DATA: list[dict[str, Any]] = [
"description": "An extension for Arista Cloud Connect gateway",
"affectedAgents": [],
"agentsToRestart": [],
},
}
}
},
{"extensions": []},
],
"inputs": None,
"expected": {"result": "failure", "messages": ["EOS extensions mismatch - Installed: AristaCloudGateway-1.0.1-1.swix Configured: Not found"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["EOS extensions mismatch - Installed: AristaCloudGateway-1.0.1-1.swix Configured: Not found"]},
},
{
"name": "failure-multiple-extensions",
"test": VerifyEOSExtensions,
(VerifyEOSExtensions, "failure-multiple-extensions"): {
"eos_data": [
{
"extensions": {
@ -190,12 +158,11 @@ DATA: list[dict[str, Any]] = [
},
{"extensions": ["AristaCloudGateway-1.0.1-1.swix", "EOS-4.33.0F-NDRSensor.swix"]},
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"EOS extensions mismatch - Installed: AristaCloudGateway-1.0.1-1.swix Configured: AristaCloudGateway-1.0.1-1.swix, EOS-4.33.0F-NDRSensor.swix"
],
},
},
]
}

View file

@ -5,8 +5,11 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.stp import (
VerifySTPBlockedPorts,
VerifySTPCounters,
@ -18,117 +21,89 @@ from anta.tests.stp import (
)
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifySTPMode,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifySTPMode, "success"): {
"eos_data": [
{"spanningTreeVlanInstances": {"10": {"spanningTreeVlanInstance": {"protocol": "rstp"}}}},
{"spanningTreeVlanInstances": {"20": {"spanningTreeVlanInstance": {"protocol": "rstp"}}}},
],
"inputs": {"mode": "rstp", "vlans": [10, 20]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-instances",
"test": VerifySTPMode,
"eos_data": [
{"spanningTreeVlanInstances": {}},
{"spanningTreeVlanInstances": {}},
],
(VerifySTPMode, "failure-no-instances"): {
"eos_data": [{"spanningTreeVlanInstances": {}}, {"spanningTreeVlanInstances": {}}],
"inputs": {"mode": "rstp", "vlans": [10, 20]},
"expected": {"result": "failure", "messages": ["VLAN 10 STP mode: rstp - Not configured", "VLAN 20 STP mode: rstp - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VLAN 10 STP mode: rstp - Not configured", "VLAN 20 STP mode: rstp - Not configured"]},
},
{
"name": "failure-wrong-mode",
"test": VerifySTPMode,
(VerifySTPMode, "failure-wrong-mode"): {
"eos_data": [
{"spanningTreeVlanInstances": {"10": {"spanningTreeVlanInstance": {"protocol": "mstp"}}}},
{"spanningTreeVlanInstances": {"20": {"spanningTreeVlanInstance": {"protocol": "mstp"}}}},
],
"inputs": {"mode": "rstp", "vlans": [10, 20]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["VLAN 10 - Incorrect STP mode - Expected: rstp Actual: mstp", "VLAN 20 - Incorrect STP mode - Expected: rstp Actual: mstp"],
},
},
{
"name": "failure-both",
"test": VerifySTPMode,
"eos_data": [
{"spanningTreeVlanInstances": {}},
{"spanningTreeVlanInstances": {"20": {"spanningTreeVlanInstance": {"protocol": "mstp"}}}},
],
(VerifySTPMode, "failure-both"): {
"eos_data": [{"spanningTreeVlanInstances": {}}, {"spanningTreeVlanInstances": {"20": {"spanningTreeVlanInstance": {"protocol": "mstp"}}}}],
"inputs": {"mode": "rstp", "vlans": [10, 20]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["VLAN 10 STP mode: rstp - Not configured", "VLAN 20 - Incorrect STP mode - Expected: rstp Actual: mstp"],
},
},
{
"name": "success",
"test": VerifySTPBlockedPorts,
"eos_data": [{"spanningTreeInstances": {}}],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifySTPBlockedPorts,
(VerifySTPBlockedPorts, "success"): {"eos_data": [{"spanningTreeInstances": {}}], "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifySTPBlockedPorts, "failure"): {
"eos_data": [{"spanningTreeInstances": {"MST0": {"spanningTreeBlockedPorts": ["Ethernet10"]}, "MST10": {"spanningTreeBlockedPorts": ["Ethernet10"]}}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["STP Instance: MST0 - Blocked ports - Ethernet10", "STP Instance: MST10 - Blocked ports - Ethernet10"]},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["STP Instance: MST0 - Blocked ports - Ethernet10", "STP Instance: MST10 - Blocked ports - Ethernet10"],
},
},
{
"name": "success",
"test": VerifySTPCounters,
(VerifySTPCounters, "success"): {
"eos_data": [{"interfaces": {"Ethernet10": {"bpduSent": 99, "bpduReceived": 0, "bpduTaggedError": 0, "bpduOtherError": 0, "bpduRateLimitCount": 0}}}],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-bpdu-tagged-error-mismatch",
"test": VerifySTPCounters,
(VerifySTPCounters, "failure-bpdu-tagged-error-mismatch"): {
"eos_data": [
{
"interfaces": {
"Ethernet10": {"bpduSent": 201, "bpduReceived": 0, "bpduTaggedError": 3, "bpduOtherError": 0, "bpduRateLimitCount": 0},
"Ethernet11": {"bpduSent": 99, "bpduReceived": 0, "bpduTaggedError": 3, "bpduOtherError": 0, "bpduRateLimitCount": 0},
},
},
}
}
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Interface Ethernet10 - STP BPDU packet tagged errors count mismatch - Expected: 0 Actual: 3",
"Interface Ethernet11 - STP BPDU packet tagged errors count mismatch - Expected: 0 Actual: 3",
],
},
},
{
"name": "failure-bpdu-other-error-mismatch",
"test": VerifySTPCounters,
(VerifySTPCounters, "failure-bpdu-other-error-mismatch"): {
"eos_data": [
{
"interfaces": {
"Ethernet10": {"bpduSent": 201, "bpduReceived": 0, "bpduTaggedError": 0, "bpduOtherError": 3, "bpduRateLimitCount": 0},
"Ethernet11": {"bpduSent": 99, "bpduReceived": 0, "bpduTaggedError": 0, "bpduOtherError": 6, "bpduRateLimitCount": 0},
},
},
}
}
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Interface Ethernet10 - STP BPDU packet other errors count mismatch - Expected: 0 Actual: 3",
"Interface Ethernet11 - STP BPDU packet other errors count mismatch - Expected: 0 Actual: 6",
],
},
},
{
"name": "success",
"test": VerifySTPForwardingPorts,
(VerifySTPForwardingPorts, "success"): {
"eos_data": [
{
"unmappedVlans": [],
@ -140,11 +115,9 @@ DATA: list[dict[str, Any]] = [
},
],
"inputs": {"vlans": [10, 20]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-vlan-not-in-topology", # Should it succeed really ? TODO - this output should be impossible
"test": VerifySTPForwardingPorts,
(VerifySTPForwardingPorts, "success-vlan-not-in-topology"): {
"eos_data": [
{
"unmappedVlans": [],
@ -156,18 +129,14 @@ DATA: list[dict[str, Any]] = [
},
],
"inputs": {"vlans": [10, 20]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-instances",
"test": VerifySTPForwardingPorts,
(VerifySTPForwardingPorts, "failure-no-instances"): {
"eos_data": [{"unmappedVlans": [], "topologies": {}}, {"unmappedVlans": [], "topologies": {}}],
"inputs": {"vlans": [10, 20]},
"expected": {"result": "failure", "messages": ["VLAN 10 - STP instance is not configured", "VLAN 20 - STP instance is not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VLAN 10 - STP instance is not configured", "VLAN 20 - STP instance is not configured"]},
},
{
"name": "failure",
"test": VerifySTPForwardingPorts,
(VerifySTPForwardingPorts, "failure"): {
"eos_data": [
{
"unmappedVlans": [],
@ -180,16 +149,14 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {"vlans": [10, 20]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"VLAN 10 Interface: Ethernet10 - Invalid state - Expected: forwarding Actual: discarding",
"VLAN 20 Interface: Ethernet10 - Invalid state - Expected: forwarding Actual: discarding",
],
},
},
{
"name": "success-specific-instances",
"test": VerifySTPRootPriority,
(VerifySTPRootPriority, "success-specific-instances"): {
"eos_data": [
{
"instances": {
@ -201,7 +168,7 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
"VL20": {
"rootBridge": {
@ -211,7 +178,7 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
"VL30": {
"rootBridge": {
@ -221,17 +188,15 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
},
},
}
}
],
"inputs": {"priority": 32768, "instances": [10, 20]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-all-instances",
"test": VerifySTPRootPriority,
(VerifySTPRootPriority, "success-all-instances"): {
"eos_data": [
{
"instances": {
@ -243,7 +208,7 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
"VL20": {
"rootBridge": {
@ -253,7 +218,7 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
"VL30": {
"rootBridge": {
@ -263,17 +228,15 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
},
},
}
}
],
"inputs": {"priority": 32768},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-MST",
"test": VerifySTPRootPriority,
(VerifySTPRootPriority, "success-MST"): {
"eos_data": [
{
"instances": {
@ -285,17 +248,15 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
},
},
},
}
}
}
}
],
"inputs": {"priority": 16384, "instances": [0]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-input-instance-none",
"test": VerifySTPRootPriority,
(VerifySTPRootPriority, "success-input-instance-none"): {
"eos_data": [
{
"instances": {
@ -307,17 +268,15 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
},
},
},
}
}
}
}
],
"inputs": {"priority": 16384},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-instances",
"test": VerifySTPRootPriority,
(VerifySTPRootPriority, "failure-no-instances"): {
"eos_data": [
{
"instances": {
@ -329,24 +288,20 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
},
},
},
}
}
}
}
],
"inputs": {"priority": 32768, "instances": [0]},
"expected": {"result": "failure", "messages": ["STP Instance: WRONG0 - Unsupported STP instance type"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["STP Instance: WRONG0 - Unsupported STP instance type"]},
},
{
"name": "failure-wrong-instance-type",
"test": VerifySTPRootPriority,
(VerifySTPRootPriority, "failure-wrong-instance-type"): {
"eos_data": [{"instances": {}}],
"inputs": {"priority": 32768, "instances": [10, 20]},
"expected": {"result": "failure", "messages": ["No STP instances configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No STP instances configured"]},
},
{
"name": "failure-instance-not-found",
"test": VerifySTPRootPriority,
(VerifySTPRootPriority, "failure-instance-not-found"): {
"eos_data": [
{
"instances": {
@ -358,17 +313,15 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
}
}
}
],
"inputs": {"priority": 32768, "instances": [11, 20]},
"expected": {"result": "failure", "messages": ["Instance: VL11 - Not configured", "Instance: VL20 - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Instance: VL11 - Not configured", "Instance: VL20 - Not configured"]},
},
{
"name": "failure-wrong-priority",
"test": VerifySTPRootPriority,
(VerifySTPRootPriority, "failure-wrong-priority"): {
"eos_data": [
{
"instances": {
@ -380,7 +333,7 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
"VL20": {
"rootBridge": {
@ -390,7 +343,7 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
"VL30": {
"rootBridge": {
@ -400,23 +353,21 @@ DATA: list[dict[str, Any]] = [
"helloTime": 2.0,
"maxAge": 20,
"forwardDelay": 15,
},
}
},
},
},
}
}
],
"inputs": {"priority": 32768, "instances": [10, 20, 30]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"STP Instance: VL20 - Incorrect root priority - Expected: 32768 Actual: 8196",
"STP Instance: VL30 - Incorrect root priority - Expected: 32768 Actual: 8196",
],
},
},
{
"name": "success-mstp",
"test": VerifyStpTopologyChanges,
(VerifyStpTopologyChanges, "success-mstp"): {
"eos_data": [
{
"unmappedVlans": [],
@ -434,14 +385,12 @@ DATA: list[dict[str, Any]] = [
}
},
},
},
}
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-rstp",
"test": VerifyStpTopologyChanges,
(VerifyStpTopologyChanges, "success-rstp"): {
"eos_data": [
{
"unmappedVlans": [],
@ -459,23 +408,19 @@ DATA: list[dict[str, Any]] = [
}
},
},
},
}
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-rapid-pvst",
"test": VerifyStpTopologyChanges,
(VerifyStpTopologyChanges, "success-rapid-pvst"): {
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"NoStp": {
"vlans": [4094, 4093, 1006],
"interfaces": {
"PeerEthernet2": {"state": "forwarding", "numChanges": 1, "lastChange": 1727151356.1330667},
},
"interfaces": {"PeerEthernet2": {"state": "forwarding", "numChanges": 1, "lastChange": 1727151356.1330667}},
},
"Vl1": {"vlans": [1], "interfaces": {"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0615358}}},
"Vl10": {
@ -527,14 +472,12 @@ DATA: list[dict[str, Any]] = [
},
},
},
},
}
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-unstable-topology",
"test": VerifyStpTopologyChanges,
(VerifyStpTopologyChanges, "failure-unstable-topology"): {
"eos_data": [
{
"unmappedVlans": [],
@ -544,22 +487,20 @@ DATA: list[dict[str, Any]] = [
"Cpu": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.735365},
"Port-Channel5": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
},
}
},
},
}
],
"inputs": {"threshold": 10},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Topology: Cist Interface: Cpu - Number of changes not within the threshold - Expected: 10 Actual: 15",
"Topology: Cist Interface: Port-Channel5 - Number of changes not within the threshold - Expected: 10 Actual: 15",
],
},
},
{
"name": "failure-topologies-not-configured",
"test": VerifyStpTopologyChanges,
(VerifyStpTopologyChanges, "failure-topologies-not-configured"): {
"eos_data": [
{
"unmappedVlans": [],
@ -571,28 +512,22 @@ DATA: list[dict[str, Any]] = [
}
}
},
},
}
],
"inputs": {"threshold": 10},
"expected": {"result": "failure", "messages": ["STP is not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["STP is not configured"]},
},
{
"name": "success",
"test": VerifySTPDisabledVlans,
(VerifySTPDisabledVlans, "success"): {
"eos_data": [{"spanningTreeVlanInstances": {"1": {"spanningTreeVlanInstance": {"protocol": "mstp", "bridge": {"priority": 32768}}}, "6": {}, "4094": {}}}],
"inputs": {"vlans": ["6", "4094"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-stp-not-configured",
"test": VerifySTPDisabledVlans,
(VerifySTPDisabledVlans, "failure-stp-not-configured"): {
"eos_data": [{"spanningTreeVlanInstances": {}}],
"inputs": {"vlans": ["6", "4094"]},
"expected": {"result": "failure", "messages": ["STP is not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["STP is not configured"]},
},
{
"name": "failure-vlans-not-found",
"test": VerifySTPDisabledVlans,
(VerifySTPDisabledVlans, "failure-vlans-not-found"): {
"eos_data": [
{
"spanningTreeVlanInstances": {
@ -603,11 +538,9 @@ DATA: list[dict[str, Any]] = [
}
],
"inputs": {"vlans": ["16", "4093"]},
"expected": {"result": "failure", "messages": ["VLAN: 16 - Not configured", "VLAN: 4093 - Not configured"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VLAN: 16 - Not configured", "VLAN: 4093 - Not configured"]},
},
{
"name": "failure-vlans-enabled",
"test": VerifySTPDisabledVlans,
(VerifySTPDisabledVlans, "failure-vlans-enabled"): {
"eos_data": [
{
"spanningTreeVlanInstances": {
@ -618,6 +551,6 @@ DATA: list[dict[str, Any]] = [
}
],
"inputs": {"vlans": ["6", "4094"]},
"expected": {"result": "failure", "messages": ["VLAN: 6 - STP is enabled", "VLAN: 4094 - STP is enabled"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["VLAN: 6 - STP is enabled", "VLAN: 4094 - STP is enabled"]},
},
]
}

View file

@ -5,48 +5,24 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.stun import VerifyStunClientTranslation, VerifyStunServer
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyStunClientTranslation,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyStunClientTranslation, "success"): {
"eos_data": [
{
"bindings": {
"000000010a64ff0100000000": {
"sourceAddress": {"ip": "100.64.3.2", "port": 4500},
"publicAddress": {"ip": "192.64.3.2", "port": 6006},
}
}
},
{
"bindings": {
"000000040a64ff0100000000": {
"sourceAddress": {"ip": "172.18.3.2", "port": 4500},
"publicAddress": {"ip": "192.18.3.2", "port": 6006},
}
}
},
{
"bindings": {
"000000040a64ff0100000000": {
"sourceAddress": {"ip": "172.18.4.2", "port": 4500},
"publicAddress": {"ip": "192.18.4.2", "port": 6006},
}
}
},
{
"bindings": {
"000000040a64ff0100000000": {
"sourceAddress": {"ip": "172.18.6.2", "port": 4500},
"publicAddress": {"ip": "192.18.6.2", "port": 6006},
}
}
},
{"bindings": {"000000010a64ff0100000000": {"sourceAddress": {"ip": "100.64.3.2", "port": 4500}, "publicAddress": {"ip": "192.64.3.2", "port": 6006}}}},
{"bindings": {"000000040a64ff0100000000": {"sourceAddress": {"ip": "172.18.3.2", "port": 4500}, "publicAddress": {"ip": "192.18.3.2", "port": 6006}}}},
{"bindings": {"000000040a64ff0100000000": {"sourceAddress": {"ip": "172.18.4.2", "port": 4500}, "publicAddress": {"ip": "192.18.4.2", "port": 6006}}}},
{"bindings": {"000000040a64ff0100000000": {"sourceAddress": {"ip": "172.18.6.2", "port": 4500}, "publicAddress": {"ip": "192.18.6.2", "port": 6006}}}},
],
"inputs": {
"stun_clients": [
@ -56,28 +32,12 @@ DATA: list[dict[str, Any]] = [
{"source_address": "172.18.6.2", "source_port": 4500, "public_port": 6006},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-incorrect-public-ip",
"test": VerifyStunClientTranslation,
(VerifyStunClientTranslation, "failure-incorrect-public-ip"): {
"eos_data": [
{
"bindings": {
"000000010a64ff0100000000": {
"sourceAddress": {"ip": "100.64.3.2", "port": 4500},
"publicAddress": {"ip": "192.64.3.2", "port": 6006},
}
}
},
{
"bindings": {
"000000040a64ff0100000000": {
"sourceAddress": {"ip": "172.18.3.2", "port": 4500},
"publicAddress": {"ip": "192.18.3.2", "port": 6006},
}
}
},
{"bindings": {"000000010a64ff0100000000": {"sourceAddress": {"ip": "100.64.3.2", "port": 4500}, "publicAddress": {"ip": "192.64.3.2", "port": 6006}}}},
{"bindings": {"000000040a64ff0100000000": {"sourceAddress": {"ip": "172.18.3.2", "port": 4500}, "publicAddress": {"ip": "192.18.3.2", "port": 6006}}}},
],
"inputs": {
"stun_clients": [
@ -86,20 +46,15 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Client 100.64.3.2 Port: 4500 - Incorrect public-facing address - Expected: 192.164.3.2 Actual: 192.64.3.2",
"Client 172.18.3.2 Port: 4500 - Incorrect public-facing address - Expected: 192.118.3.2 Actual: 192.18.3.2",
],
},
},
{
"name": "failure-no-client",
"test": VerifyStunClientTranslation,
"eos_data": [
{"bindings": {}},
{"bindings": {}},
],
(VerifyStunClientTranslation, "failure-no-client"): {
"eos_data": [{"bindings": {}}, {"bindings": {}}],
"inputs": {
"stun_clients": [
{"source_address": "100.64.3.2", "public_address": "192.164.3.2", "source_port": 4500, "public_port": 6006},
@ -107,23 +62,14 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Client 100.64.3.2 Port: 4500 - STUN client translation not found", "Client 172.18.3.2 Port: 4500 - STUN client translation not found"],
},
},
{
"name": "failure-incorrect-public-port",
"test": VerifyStunClientTranslation,
(VerifyStunClientTranslation, "failure-incorrect-public-port"): {
"eos_data": [
{"bindings": {}},
{
"bindings": {
"000000040a64ff0100000000": {
"sourceAddress": {"ip": "172.18.3.2", "port": 4500},
"publicAddress": {"ip": "192.18.3.2", "port": 4800},
}
}
},
{"bindings": {"000000040a64ff0100000000": {"sourceAddress": {"ip": "172.18.3.2", "port": 4500}, "publicAddress": {"ip": "192.18.3.2", "port": 4800}}}},
],
"inputs": {
"stun_clients": [
@ -132,7 +78,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Client 100.64.3.2 Port: 4500 - STUN client translation not found",
"Client 172.18.3.2 Port: 4500 - Incorrect public-facing address - Expected: 192.118.3.2 Actual: 192.18.3.2",
@ -140,19 +86,10 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "failure-all-type",
"test": VerifyStunClientTranslation,
(VerifyStunClientTranslation, "failure-all-type"): {
"eos_data": [
{"bindings": {}},
{
"bindings": {
"000000040a64ff0100000000": {
"sourceAddress": {"ip": "172.18.3.2", "port": 4500},
"publicAddress": {"ip": "192.18.3.2", "port": 4800},
}
}
},
{"bindings": {"000000040a64ff0100000000": {"sourceAddress": {"ip": "172.18.3.2", "port": 4500}, "publicAddress": {"ip": "192.18.3.2", "port": 4800}}}},
],
"inputs": {
"stun_clients": [
@ -161,7 +98,7 @@ DATA: list[dict[str, Any]] = [
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Client 100.64.3.2 Port: 4500 - STUN client translation not found",
"Client 172.18.4.2 Port: 4800 - Incorrect public-facing address - Expected: 192.118.3.2 Actual: 192.18.3.2",
@ -169,61 +106,20 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyStunServer,
"eos_data": [
{
"enabled": True,
"pid": 1895,
}
],
(VerifyStunServer, "success"): {"eos_data": [{"enabled": True, "pid": 1895}], "inputs": {}, "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyStunServer, "failure-disabled"): {
"eos_data": [{"enabled": False, "pid": 1895}],
"inputs": {},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["STUN server status is disabled"]},
},
{
"name": "failure-disabled",
"test": VerifyStunServer,
"eos_data": [
{
"enabled": False,
"pid": 1895,
}
],
(VerifyStunServer, "failure-not-running"): {
"eos_data": [{"enabled": True, "pid": 0}],
"inputs": {},
"expected": {
"result": "failure",
"messages": ["STUN server status is disabled"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["STUN server is not running"]},
},
{
"name": "failure-not-running",
"test": VerifyStunServer,
"eos_data": [
{
"enabled": True,
"pid": 0,
}
],
(VerifyStunServer, "failure-not-running-disabled"): {
"eos_data": [{"enabled": False, "pid": 0}],
"inputs": {},
"expected": {
"result": "failure",
"messages": ["STUN server is not running"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["STUN server status is disabled and not running"]},
},
{
"name": "failure-not-running-disabled",
"test": VerifyStunServer,
"eos_data": [
{
"enabled": False,
"pid": 0,
}
],
"inputs": {},
"expected": {
"result": "failure",
"messages": ["STUN server status is disabled and not running"],
},
},
]
}

View file

@ -5,8 +5,11 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.system import (
VerifyAgentLogs,
VerifyCoredump,
@ -21,127 +24,137 @@ from anta.tests.system import (
)
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyUptime,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyUptime, "success"): {
"eos_data": [{"upTime": 1186689.15, "loadAvg": [0.13, 0.12, 0.09], "users": 1, "currentTime": 1683186659.139859}],
"inputs": {"minimum": 666},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyUptime,
(VerifyUptime, "failure"): {
"eos_data": [{"upTime": 665.15, "loadAvg": [0.13, 0.12, 0.09], "users": 1, "currentTime": 1683186659.139859}],
"inputs": {"minimum": 666},
"expected": {"result": "failure", "messages": ["Device uptime is incorrect - Expected: 666s Actual: 665.15s"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device uptime is incorrect - Expected: 666s Actual: 665.15s"]},
},
{
"name": "success-no-reload",
"test": VerifyReloadCause,
(VerifyReloadCause, "success-no-reload"): {
"eos_data": [{"kernelCrashData": [], "resetCauses": [], "full": False}],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-valid-cause",
"test": VerifyReloadCause,
(VerifyReloadCause, "success-valid-cause-user"): {
"eos_data": [
{
"resetCauses": [
{"recommendedAction": "No action necessary.", "description": "Reload requested by the user.", "timestamp": 1683186892.0, "debugInfoIsDir": False}
],
"full": False,
}
],
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyReloadCause, "success-valid-reload-cause-ztp"): {
"eos_data": [
{
"resetCauses": [
{
"description": "System reloaded due to Zero Touch Provisioning",
"timestamp": 1729856740.0,
"recommendedAction": "No action necessary.",
"description": "Reload requested by the user.",
"timestamp": 1683186892.0,
"debugInfoIsDir": False,
},
}
],
"full": False,
},
}
],
"inputs": None,
"expected": {"result": "success"},
"inputs": {"allowed_causes": ["ZTP"]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyReloadCause,
# The failure cause is made up
(VerifyReloadCause, "success-valid-reload-cause-fpga"): {
"eos_data": [
{
"resetCauses": [
{"recommendedAction": "No action necessary.", "description": "Reload after crash.", "timestamp": 1683186892.0, "debugInfoIsDir": False},
{
"description": "Reload requested after FPGA upgrade",
"timestamp": 1729856740.0,
"recommendedAction": "No action necessary.",
"debugInfoIsDir": False,
}
],
"full": False,
},
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Reload cause is: Reload after crash."]},
"inputs": {"allowed_causes": ["fpga"]},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-without-minidump",
"test": VerifyCoredump,
"eos_data": [{"mode": "compressedDeferred", "coreFiles": []}],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "success-with-minidump",
"test": VerifyCoredump,
"eos_data": [{"mode": "compressedDeferred", "coreFiles": ["minidump"]}],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure-without-minidump",
"test": VerifyCoredump,
"eos_data": [{"mode": "compressedDeferred", "coreFiles": ["core.2344.1584483862.Mlag.gz", "core.23101.1584483867.Mlag.gz"]}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Core dump(s) have been found: core.2344.1584483862.Mlag.gz, core.23101.1584483867.Mlag.gz"]},
},
{
"name": "failure-with-minidump",
"test": VerifyCoredump,
"eos_data": [{"mode": "compressedDeferred", "coreFiles": ["minidump", "core.2344.1584483862.Mlag.gz", "core.23101.1584483867.Mlag.gz"]}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Core dump(s) have been found: core.2344.1584483862.Mlag.gz, core.23101.1584483867.Mlag.gz"]},
},
{
"name": "success",
"test": VerifyAgentLogs,
"eos_data": [""],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyAgentLogs,
(VerifyReloadCause, "failure-invalid-reload-cause"): {
"eos_data": [
"""===> /var/log/agents/Test-666 Thu May 4 09:57:02 2023 <===
CLI Exception: Exception
CLI Exception: Backtrace
===> /var/log/agents/Aaa-855 Fri Jul 7 15:07:00 2023 <===
===== Output from /usr/bin/Aaa [] (PID=855) started Jul 7 15:06:11.606414 ===
EntityManager::doBackoff waiting for remote sysdb version ....ok
===> /var/log/agents/Acl-830 Fri Jul 7 15:07:00 2023 <===
===== Output from /usr/bin/Acl [] (PID=830) started Jul 7 15:06:10.871700 ===
EntityManager::doBackoff waiting for remote sysdb version ...................ok
""",
{
"resetCauses": [
{
"description": "Reload requested after FPGA upgrade",
"timestamp": 1729856740.0,
"recommendedAction": "No action necessary.",
"debugInfoIsDir": False,
}
],
"full": False,
}
],
"inputs": None,
"inputs": {"allowed_causes": ["ZTP"]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Invalid reload cause - Expected: 'System reloaded due to Zero Touch Provisioning' Actual: 'Reload requested after FPGA upgrade'"],
},
},
(VerifyReloadCause, "failure"): {
"eos_data": [
{
"resetCauses": [
{"recommendedAction": "No action necessary.", "description": "Reload after crash.", "timestamp": 1683186892.0, "debugInfoIsDir": False}
],
"full": False,
}
],
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Invalid reload cause - Expected: 'Reload requested by the user.', 'Reload requested after FPGA upgrade' Actual: 'Reload after crash.'"],
},
},
(VerifyCoredump, "success-without-minidump"): {
"eos_data": [{"mode": "compressedDeferred", "coreFiles": []}],
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyCoredump, "success-with-minidump"): {
"eos_data": [{"mode": "compressedDeferred", "coreFiles": ["minidump"]}],
"expected": {"result": AntaTestStatus.SUCCESS},
},
(VerifyCoredump, "failure-without-minidump"): {
"eos_data": [{"mode": "compressedDeferred", "coreFiles": ["core.2344.1584483862.Mlag.gz", "core.23101.1584483867.Mlag.gz"]}],
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Core dump(s) have been found: core.2344.1584483862.Mlag.gz, core.23101.1584483867.Mlag.gz"]},
},
(VerifyCoredump, "failure-with-minidump"): {
"eos_data": [{"mode": "compressedDeferred", "coreFiles": ["minidump", "core.2344.1584483862.Mlag.gz", "core.23101.1584483867.Mlag.gz"]}],
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Core dump(s) have been found: core.2344.1584483862.Mlag.gz, core.23101.1584483867.Mlag.gz"]},
},
(VerifyAgentLogs, "success"): {"eos_data": [""], "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyAgentLogs, "failure"): {
"eos_data": [
"===> /var/log/agents/Test-666 Thu May 4 09:57:02 2023 <===\nCLI Exception: Exception\nCLI Exception: Backtrace\n===> /var/log/agents/Aaa-855"
" Fri Jul 7 15:07:00 2023 <===\n===== Output from /usr/bin/Aaa [] (PID=855) started Jul 7 15:06:11.606414 ===\n"
"EntityManager::doBackoff waiting for remote sysdb version ....ok\n\n===> /var/log/agents/Acl-830"
" Fri Jul 7 15:07:00 2023 <===\n===== Output from /usr/bin/Acl [] (PID=830) started Jul 7 15:06:10.871700 ===\n"
"EntityManager::doBackoff waiting for remote sysdb version ...................ok\n"
],
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": [
"Device has reported agent crashes:\n"
" * /var/log/agents/Test-666 Thu May 4 09:57:02 2023\n"
" * /var/log/agents/Aaa-855 Fri Jul 7 15:07:00 2023\n"
" * /var/log/agents/Acl-830 Fri Jul 7 15:07:00 2023",
"Device has reported agent crashes:\n * /var/log/agents/Test-666 Thu May 4 09:57:02 2023\n"
" * /var/log/agents/Aaa-855 Fri Jul 7 15:07:00 2023\n * /var/log/agents/Acl-830 Fri Jul 7 15:07:00 2023"
],
},
},
{
"name": "success",
"test": VerifyCPUUtilization,
(VerifyCPUUtilization, "success"): {
"eos_data": [
{
"cpuInfo": {"%Cpu(s)": {"idle": 88.2, "stolen": 0.0, "user": 5.9, "swIrq": 0.0, "ioWait": 0.0, "system": 0.0, "hwIrq": 5.9, "nice": 0.0}},
@ -159,16 +172,13 @@ EntityManager::doBackoff waiting for remote sysdb version ...................ok
"activeTime": 360,
"virtMem": "6644",
"sharedMem": "3996",
},
}
},
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyCPUUtilization,
(VerifyCPUUtilization, "failure"): {
"eos_data": [
{
"cpuInfo": {"%Cpu(s)": {"idle": 24.8, "stolen": 0.0, "user": 5.9, "swIrq": 0.0, "ioWait": 0.0, "system": 0.0, "hwIrq": 5.9, "nice": 0.0}},
@ -186,122 +196,56 @@ EntityManager::doBackoff waiting for remote sysdb version ...................ok
"activeTime": 360,
"virtMem": "6644",
"sharedMem": "3996",
},
}
},
},
}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Device has reported a high CPU utilization - Expected: < 75% Actual: 75.2%"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device has reported a high CPU utilization - Expected: < 75% Actual: 75.2%"]},
},
{
"name": "success",
"test": VerifyMemoryUtilization,
(VerifyMemoryUtilization, "success"): {
"eos_data": [
{
"uptime": 1994.67,
"modelName": "vEOS-lab",
"internalVersion": "4.27.3F-26379303.4273F",
"memTotal": 2004568,
"memFree": 879004,
"version": "4.27.3F",
},
{"uptime": 1994.67, "modelName": "vEOS-lab", "internalVersion": "4.27.3F-26379303.4273F", "memTotal": 2004568, "memFree": 879004, "version": "4.27.3F"}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyMemoryUtilization,
(VerifyMemoryUtilization, "failure"): {
"eos_data": [
{
"uptime": 1994.67,
"modelName": "vEOS-lab",
"internalVersion": "4.27.3F-26379303.4273F",
"memTotal": 2004568,
"memFree": 89004,
"version": "4.27.3F",
},
{"uptime": 1994.67, "modelName": "vEOS-lab", "internalVersion": "4.27.3F-26379303.4273F", "memTotal": 2004568, "memFree": 89004, "version": "4.27.3F"}
],
"inputs": None,
"expected": {"result": "failure", "messages": ["Device has reported a high memory usage - Expected: < 75% Actual: 95.56%"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Device has reported a high memory usage - Expected: < 75% Actual: 95.56%"]},
},
{
"name": "success",
"test": VerifyFileSystemUtilization,
(VerifyFileSystemUtilization, "success"): {
"eos_data": [
"""Filesystem Size Used Avail Use% Mounted on
/dev/sda2 3.9G 988M 2.9G 26% /mnt/flash
none 294M 78M 217M 27% /
none 294M 78M 217M 27% /.overlay
/dev/loop0 461M 461M 0 100% /rootfs-i386
""",
"Filesystem Size Used Avail Use% Mounted on\n/dev/sda2 3.9G 988M 2.9G 26% /mnt/flash\nnone 294M 78M 217M 27% /\n"
"none 294M 78M 217M 27% /.overlay\n/dev/loop0 461M 461M 0 100% /rootfs-i386\n"
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyFileSystemUtilization,
(VerifyFileSystemUtilization, "failure"): {
"eos_data": [
"""Filesystem Size Used Avail Use% Mounted on
/dev/sda2 3.9G 988M 2.9G 84% /mnt/flash
none 294M 78M 217M 27% /
none 294M 78M 217M 84% /.overlay
/dev/loop0 461M 461M 0 100% /rootfs-i386
""",
"Filesystem Size Used Avail Use% Mounted on\n/dev/sda2 3.9G 988M 2.9G 84% /mnt/flash\nnone 294M 78M 217M 27% /\n"
"none 294M 78M 217M 84% /.overlay\n/dev/loop0 461M 461M 0 100% /rootfs-i386\n"
],
"inputs": None,
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Mount point: /dev/sda2 3.9G 988M 2.9G 84% /mnt/flash - Higher disk space utilization - Expected: 75% Actual: 84%",
"Mount point: none 294M 78M 217M 84% /.overlay - Higher disk space utilization - Expected: 75% Actual: 84%",
],
},
},
{
"name": "success",
"test": VerifyNTP,
"eos_data": [
"""synchronised
poll interval unknown
""",
],
"inputs": None,
"expected": {"result": "success"},
(VerifyNTP, "success"): {"eos_data": ["synchronised\npoll interval unknown\n"], "expected": {"result": AntaTestStatus.SUCCESS}},
(VerifyNTP, "failure"): {
"eos_data": ["unsynchronised\npoll interval unknown\n"],
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["NTP status mismatch - Expected: synchronised Actual: unsynchronised"]},
},
{
"name": "failure",
"test": VerifyNTP,
"eos_data": [
"""unsynchronised
poll interval unknown
""",
],
"inputs": None,
"expected": {"result": "failure", "messages": ["NTP status mismatch - Expected: synchronised Actual: unsynchronised"]},
},
{
"name": "success",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "success"): {
"eos_data": [
{
"peers": {
"1.1.1.1": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
},
"2.2.2.2": {
"condition": "candidate",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 2,
},
"3.3.3.3": {
"condition": "candidate",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 2,
},
"1.1.1.1": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 1},
"2.2.2.2": {"condition": "candidate", "peerIpAddr": "2.2.2.2", "stratumLevel": 2},
"3.3.3.3": {"condition": "candidate", "peerIpAddr": "3.3.3.3", "stratumLevel": 2},
}
}
],
@ -312,29 +256,15 @@ poll interval unknown
{"server_address": "3.3.3.3", "stratum": 2},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-pool-name",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "success-pool-name"): {
"eos_data": [
{
"peers": {
"1.ntp.networks.com": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
},
"2.ntp.networks.com": {
"condition": "candidate",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 2,
},
"3.ntp.networks.com": {
"condition": "candidate",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 2,
},
"1.ntp.networks.com": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 1},
"2.ntp.networks.com": {"condition": "candidate", "peerIpAddr": "2.2.2.2", "stratumLevel": 2},
"3.ntp.networks.com": {"condition": "candidate", "peerIpAddr": "3.3.3.3", "stratumLevel": 2},
}
}
],
@ -345,56 +275,28 @@ poll interval unknown
{"server_address": "3.ntp.networks.com", "stratum": 2},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-ntp-pool-as-input",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "success-ntp-pool-as-input"): {
"eos_data": [
{
"peers": {
"1.1.1.1": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
},
"2.2.2.2": {
"condition": "candidate",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 2,
},
"3.3.3.3": {
"condition": "candidate",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 2,
},
"1.1.1.1": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 1},
"2.2.2.2": {"condition": "candidate", "peerIpAddr": "2.2.2.2", "stratumLevel": 2},
"3.3.3.3": {"condition": "candidate", "peerIpAddr": "3.3.3.3", "stratumLevel": 2},
}
}
],
"inputs": {"ntp_pool": {"server_addresses": ["1.1.1.1", "2.2.2.2", "3.3.3.3"], "preferred_stratum_range": [1, 2]}},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-ntp-pool-hostname",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "success-ntp-pool-hostname"): {
"eos_data": [
{
"peers": {
"itsys-ntp010p.aristanetworks.com": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
},
"itsys-ntp011p.aristanetworks.com": {
"condition": "candidate",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 2,
},
"itsys-ntp012p.aristanetworks.com": {
"condition": "candidate",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 2,
},
"itsys-ntp010p.aristanetworks.com": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 1},
"itsys-ntp011p.aristanetworks.com": {"condition": "candidate", "peerIpAddr": "2.2.2.2", "stratumLevel": 2},
"itsys-ntp012p.aristanetworks.com": {"condition": "candidate", "peerIpAddr": "3.3.3.3", "stratumLevel": 2},
}
}
],
@ -404,29 +306,15 @@ poll interval unknown
"preferred_stratum_range": [1, 2],
}
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-ip-dns",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "success-ip-dns"): {
"eos_data": [
{
"peers": {
"1.1.1.1 (1.ntp.networks.com)": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
},
"2.2.2.2 (2.ntp.networks.com)": {
"condition": "candidate",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 2,
},
"3.3.3.3 (3.ntp.networks.com)": {
"condition": "candidate",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 2,
},
"1.1.1.1 (1.ntp.networks.com)": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 1},
"2.2.2.2 (2.ntp.networks.com)": {"condition": "candidate", "peerIpAddr": "2.2.2.2", "stratumLevel": 2},
"3.3.3.3 (3.ntp.networks.com)": {"condition": "candidate", "peerIpAddr": "3.3.3.3", "stratumLevel": 2},
}
}
],
@ -437,29 +325,15 @@ poll interval unknown
{"server_address": "3.3.3.3", "stratum": 2},
]
},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-ntp-server",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "failure-ntp-server"): {
"eos_data": [
{
"peers": {
"1.1.1.1": {
"condition": "candidate",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 2,
},
"2.2.2.2": {
"condition": "sys.peer",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 2,
},
"3.3.3.3": {
"condition": "sys.peer",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 3,
},
"1.1.1.1": {"condition": "candidate", "peerIpAddr": "1.1.1.1", "stratumLevel": 2},
"2.2.2.2": {"condition": "sys.peer", "peerIpAddr": "2.2.2.2", "stratumLevel": 2},
"3.3.3.3": {"condition": "sys.peer", "peerIpAddr": "3.3.3.3", "stratumLevel": 3},
}
}
],
@ -471,7 +345,7 @@ poll interval unknown
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"NTP Server: 1.1.1.1 Preferred: True Stratum: 1 - Incorrect condition - Expected: sys.peer Actual: candidate",
"NTP Server: 1.1.1.1 Preferred: True Stratum: 1 - Incorrect stratum level - Expected: 1 Actual: 2",
@ -481,9 +355,7 @@ poll interval unknown
],
},
},
{
"name": "failure-no-peers",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "failure-no-peers"): {
"eos_data": [{"peers": {}}],
"inputs": {
"ntp_servers": [
@ -492,27 +364,14 @@ poll interval unknown
{"server_address": "3.3.3.3", "stratum": 1},
]
},
"expected": {
"result": "failure",
"messages": ["No NTP peers configured"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["No NTP peers configured"]},
},
{
"name": "failure-one-peer-not-found",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "failure-one-peer-not-found"): {
"eos_data": [
{
"peers": {
"1.1.1.1": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
},
"2.2.2.2": {
"condition": "candidate",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 1,
},
"1.1.1.1": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 1},
"2.2.2.2": {"condition": "candidate", "peerIpAddr": "2.2.2.2", "stratumLevel": 1},
}
}
],
@ -523,25 +382,10 @@ poll interval unknown
{"server_address": "3.3.3.3", "stratum": 1},
]
},
"expected": {
"result": "failure",
"messages": ["NTP Server: 3.3.3.3 Preferred: False Stratum: 1 - Not configured"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["NTP Server: 3.3.3.3 Preferred: False Stratum: 1 - Not configured"]},
},
{
"name": "failure-with-two-peers-not-found",
"test": VerifyNTPAssociations,
"eos_data": [
{
"peers": {
"1.1.1.1": {
"condition": "candidate",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
}
}
}
],
(VerifyNTPAssociations, "failure-with-two-peers-not-found"): {
"eos_data": [{"peers": {"1.1.1.1": {"condition": "candidate", "peerIpAddr": "1.1.1.1", "stratumLevel": 1}}}],
"inputs": {
"ntp_servers": [
{"server_address": "1.1.1.1", "preferred": True, "stratum": 1},
@ -550,7 +394,7 @@ poll interval unknown
]
},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"NTP Server: 1.1.1.1 Preferred: True Stratum: 1 - Incorrect condition - Expected: sys.peer Actual: candidate",
"NTP Server: 2.2.2.2 Preferred: False Stratum: 1 - Not configured",
@ -558,96 +402,51 @@ poll interval unknown
],
},
},
{
"name": "failure-ntp-pool-as-input",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "failure-ntp-pool-as-input"): {
"eos_data": [
{
"peers": {
"ntp1.pool": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
},
"ntp2.pool": {
"condition": "candidate",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 2,
},
"ntp3.pool": {
"condition": "candidate",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 2,
},
"ntp1.pool": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 1},
"ntp2.pool": {"condition": "candidate", "peerIpAddr": "2.2.2.2", "stratumLevel": 2},
"ntp3.pool": {"condition": "candidate", "peerIpAddr": "3.3.3.3", "stratumLevel": 2},
}
}
],
"inputs": {"ntp_pool": {"server_addresses": ["1.1.1.1", "2.2.2.2"], "preferred_stratum_range": [1, 2]}},
"expected": {
"result": "failure",
"messages": ["NTP Server: 3.3.3.3 Hostname: ntp3.pool - Associated but not part of the provided NTP pool"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["NTP Server: 3.3.3.3 Hostname: ntp3.pool - Associated but not part of the provided NTP pool"]},
},
{
"name": "failure-ntp-pool-as-input-bad-association",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "failure-ntp-pool-as-input-bad-association"): {
"eos_data": [
{
"peers": {
"ntp1.pool": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 1,
},
"ntp2.pool": {
"condition": "candidate",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 2,
},
"ntp3.pool": {
"condition": "reject",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 3,
},
"ntp1.pool": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 1},
"ntp2.pool": {"condition": "candidate", "peerIpAddr": "2.2.2.2", "stratumLevel": 2},
"ntp3.pool": {"condition": "reject", "peerIpAddr": "3.3.3.3", "stratumLevel": 3},
}
}
],
"inputs": {"ntp_pool": {"server_addresses": ["1.1.1.1", "2.2.2.2", "3.3.3.3"], "preferred_stratum_range": [1, 2]}},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"NTP Server: 3.3.3.3 Hostname: ntp3.pool - Incorrect condition - Expected: sys.peer, candidate Actual: reject",
"NTP Server: 3.3.3.3 Hostname: ntp3.pool - Incorrect stratum level - Expected Stratum Range: 1 to 2 Actual: 3",
],
},
},
{
"name": "failure-ntp-pool-hostname",
"test": VerifyNTPAssociations,
(VerifyNTPAssociations, "failure-ntp-pool-hostname"): {
"eos_data": [
{
"peers": {
"itsys-ntp010p.aristanetworks.com": {
"condition": "sys.peer",
"peerIpAddr": "1.1.1.1",
"stratumLevel": 5,
},
"itsys-ntp011p.aristanetworks.com": {
"condition": "reject",
"peerIpAddr": "2.2.2.2",
"stratumLevel": 4,
},
"itsys-ntp012p.aristanetworks.com": {
"condition": "candidate",
"peerIpAddr": "3.3.3.3",
"stratumLevel": 2,
},
"itsys-ntp010p.aristanetworks.com": {"condition": "sys.peer", "peerIpAddr": "1.1.1.1", "stratumLevel": 5},
"itsys-ntp011p.aristanetworks.com": {"condition": "reject", "peerIpAddr": "2.2.2.2", "stratumLevel": 4},
"itsys-ntp012p.aristanetworks.com": {"condition": "candidate", "peerIpAddr": "3.3.3.3", "stratumLevel": 2},
}
}
],
"inputs": {"ntp_pool": {"server_addresses": ["itsys-ntp010p.aristanetworks.com", "itsys-ntp011p.aristanetworks.com"], "preferred_stratum_range": [1, 2]}},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"NTP Server: 1.1.1.1 Hostname: itsys-ntp010p.aristanetworks.com - Incorrect stratum level - Expected Stratum Range: 1 to 2 Actual: 5",
"NTP Server: 2.2.2.2 Hostname: itsys-ntp011p.aristanetworks.com - Incorrect condition - Expected: sys.peer, candidate Actual: reject",
@ -656,23 +455,11 @@ poll interval unknown
],
},
},
{
"name": "success-no-maintenance-configured",
"test": VerifyMaintenance,
"eos_data": [
{
"units": {},
"interfaces": {},
"vrfs": {},
"warnings": ["Maintenance Mode is disabled."],
},
],
"inputs": None,
"expected": {"result": "success"},
(VerifyMaintenance, "success-no-maintenance-configured"): {
"eos_data": [{"units": {}, "interfaces": {}, "vrfs": {}, "warnings": ["Maintenance Mode is disabled."]}],
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-maintenance-configured-but-not-enabled",
"test": VerifyMaintenance,
(VerifyMaintenance, "success-maintenance-configured-but-not-enabled"): {
"eos_data": [
{
"units": {
@ -688,14 +475,11 @@ poll interval unknown
},
"interfaces": {},
"vrfs": {},
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "success-multiple-units-but-not-enabled",
"test": VerifyMaintenance,
(VerifyMaintenance, "success-multiple-units-but-not-enabled"): {
"eos_data": [
{
"units": {
@ -720,14 +504,11 @@ poll interval unknown
},
"interfaces": {},
"vrfs": {},
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-maintenance-enabled",
"test": VerifyMaintenance,
(VerifyMaintenance, "failure-maintenance-enabled"): {
"eos_data": [
{
"units": {
@ -752,20 +533,11 @@ poll interval unknown
},
"interfaces": {},
"vrfs": {},
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": [
"Units under maintenance: 'mlag'.",
"Possible causes: 'Quiesce is configured'.",
],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Units under maintenance: 'mlag'", "Possible causes: 'Quiesce is configured'"]},
},
{
"name": "failure-multiple-reasons",
"test": VerifyMaintenance,
(VerifyMaintenance, "failure-multiple-reasons"): {
"eos_data": [
{
"units": {
@ -790,21 +562,14 @@ poll interval unknown
},
"interfaces": {},
"vrfs": {},
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": [
"Units under maintenance: 'mlag'.",
"Units entering maintenance: 'System'.",
"Possible causes: 'Quiesce is configured'.",
],
"result": AntaTestStatus.FAILURE,
"messages": ["Units under maintenance: 'mlag'", "Units entering maintenance: 'System'", "Possible causes: 'Quiesce is configured'"],
},
},
{
"name": "failure-onboot-maintenance",
"test": VerifyMaintenance,
(VerifyMaintenance, "failure-onboot-maintenance"): {
"eos_data": [
{
"units": {
@ -820,20 +585,14 @@ poll interval unknown
},
"interfaces": {},
"vrfs": {},
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": [
"Units under maintenance: 'System'.",
"Possible causes: 'On-boot maintenance is configured, Quiesce is configured'.",
],
"result": AntaTestStatus.FAILURE,
"messages": ["Units under maintenance: 'System'", "Possible causes: 'On-boot maintenance is configured, Quiesce is configured'"],
},
},
{
"name": "failure-entering-maintenance-interface-violation",
"test": VerifyMaintenance,
(VerifyMaintenance, "failure-entering-maintenance-interface-violation"): {
"eos_data": [
{
"units": {
@ -849,15 +608,11 @@ poll interval unknown
},
"interfaces": {},
"vrfs": {},
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": [
"Units entering maintenance: 'System'.",
"Possible causes: 'Interface traffic threshold violation, Quiesce is configured'.",
],
"result": AntaTestStatus.FAILURE,
"messages": ["Units entering maintenance: 'System'", "Possible causes: 'Interface traffic threshold violation, Quiesce is configured'"],
},
},
]
}

View file

@ -5,88 +5,109 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.tests.vlan import VerifyDynamicVlanSource, VerifyVlanInternalPolicy
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.vlan import VerifyDynamicVlanSource, VerifyVlanInternalPolicy, VerifyVlanStatus
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyVlanInternalPolicy,
if TYPE_CHECKING:
from tests.units.anta_tests import AntaUnitTestDataDict
DATA: AntaUnitTestDataDict = {
(VerifyVlanInternalPolicy, "success"): {
"eos_data": [{"policy": "ascending", "startVlanId": 1006, "endVlanId": 4094}],
"inputs": {"policy": "ascending", "start_vlan_id": 1006, "end_vlan_id": 4094},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-incorrect-policy",
"test": VerifyVlanInternalPolicy,
(VerifyVlanInternalPolicy, "failure-incorrect-policy"): {
"eos_data": [{"policy": "descending", "startVlanId": 4094, "endVlanId": 1006}],
"inputs": {"policy": "ascending", "start_vlan_id": 1006, "end_vlan_id": 4094},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": ["Incorrect VLAN internal allocation policy configured - Expected: ascending Actual: descending"],
},
},
{
"name": "failure-incorrect-start-end-id",
"test": VerifyVlanInternalPolicy,
(VerifyVlanInternalPolicy, "failure-incorrect-start-end-id"): {
"eos_data": [{"policy": "ascending", "startVlanId": 4094, "endVlanId": 1006}],
"inputs": {"policy": "ascending", "start_vlan_id": 1006, "end_vlan_id": 4094},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"VLAN internal allocation policy: ascending - Incorrect start VLAN id configured - Expected: 1006 Actual: 4094",
"VLAN internal allocation policy: ascending - Incorrect end VLAN id configured - Expected: 4094 Actual: 1006",
],
},
},
{
"name": "success",
"test": VerifyDynamicVlanSource,
(VerifyDynamicVlanSource, "success"): {
"eos_data": [{"dynamicVlans": {"evpn": {"vlanIds": [1199]}, "mlagsync": {"vlanIds": [1401]}, "vccbfd": {"vlanIds": [1501]}}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": False},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-dynamic-vlan-sources",
"test": VerifyDynamicVlanSource,
(VerifyDynamicVlanSource, "failure-no-dynamic-vlan-sources"): {
"eos_data": [{"dynamicVlans": {}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": False},
"expected": {"result": "failure", "messages": ["Dynamic VLAN source(s) not found in configuration: evpn, mlagsync"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Dynamic VLAN source(s) not found in configuration: evpn, mlagsync"]},
},
{
"name": "failure-dynamic-vlan-sources-mismatch",
"test": VerifyDynamicVlanSource,
(VerifyDynamicVlanSource, "failure-dynamic-vlan-sources-mismatch"): {
"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"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Dynamic VLAN source(s) not found in configuration: evpn"]},
},
{
"name": "success-strict-mode",
"test": VerifyDynamicVlanSource,
(VerifyDynamicVlanSource, "success-strict-mode"): {
"eos_data": [{"dynamicVlans": {"evpn": {"vlanIds": [1199]}, "mlagsync": {"vlanIds": [1502], "vccbfd": {"vlanIds": []}}}}],
"inputs": {"sources": ["evpn", "mlagsync"], "strict": True},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-all-sources-exact-match-additional-source-found",
"test": VerifyDynamicVlanSource,
(VerifyDynamicVlanSource, "failure-all-sources-exact-match-additional-source-found"): {
"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"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Strict mode enabled: Unexpected sources have VLANs allocated: vccbfd"]},
},
{
"name": "failure-all-sources-exact-match-expected-source-not-found",
"test": VerifyDynamicVlanSource,
(VerifyDynamicVlanSource, "failure-all-sources-exact-match-expected-source-not-found"): {
"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"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Dynamic VLAN source(s) exist but have no VLANs allocated: mlagsync"]},
},
]
(VerifyVlanStatus, "success"): {
"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": AntaTestStatus.SUCCESS},
},
(VerifyVlanStatus, "failure-vlan-not-conifgured"): {
"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": AntaTestStatus.FAILURE, "messages": ["VLAN: Vlan4092 - Not configured", "VLAN: Vlan4094 - Not configured"]},
},
(VerifyVlanStatus, "failure-incorrect-status"): {
"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": AntaTestStatus.FAILURE,
"messages": [
"VLAN: Vlan4092 - Incorrect administrative status - Expected: active Actual: suspended",
"VLAN: Vlan4094 - Incorrect administrative status - Expected: suspended Actual: active",
],
},
},
}

View file

@ -5,50 +5,47 @@
from __future__ import annotations
from typing import Any
import sys
from typing import TYPE_CHECKING, Any
from anta.models import AntaTest
from anta.result_manager.models import AntaTestStatus
from anta.tests.vxlan import VerifyVxlan1ConnSettings, VerifyVxlan1Interface, VerifyVxlanConfigSanity, VerifyVxlanVniBinding, VerifyVxlanVtep
from tests.units.anta_tests import test
from tests.units.anta_tests import AntaUnitTest, test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyVxlan1Interface,
if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
TypeAlias = type
AntaUnitTestDataDict: TypeAlias = dict[tuple[type[AntaTest], str], AntaUnitTest]
DATA: AntaUnitTestDataDict = {
(VerifyVxlan1Interface, "success"): {
"eos_data": [{"interfaceDescriptions": {"Vxlan1": {"lineProtocolStatus": "up", "interfaceStatus": "up"}}}],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "skipped",
"test": VerifyVxlan1Interface,
(VerifyVxlan1Interface, "skipped"): {
"eos_data": [{"interfaceDescriptions": {"Loopback0": {"lineProtocolStatus": "up", "interfaceStatus": "up"}}}],
"inputs": None,
"expected": {"result": "skipped", "messages": ["Interface: Vxlan1 - Not configured"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Interface: Vxlan1 - Not configured"]},
},
{
"name": "failure-down-up",
"test": VerifyVxlan1Interface,
(VerifyVxlan1Interface, "failure-down-up"): {
"eos_data": [{"interfaceDescriptions": {"Vxlan1": {"lineProtocolStatus": "down", "interfaceStatus": "up"}}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Interface: Vxlan1 - Incorrect Line protocol status/Status - Expected: up/up Actual: down/up"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Interface: Vxlan1 - Incorrect Line protocol status/Status - Expected: up/up Actual: down/up"]},
},
{
"name": "failure-up-down",
"test": VerifyVxlan1Interface,
(VerifyVxlan1Interface, "failure-up-down"): {
"eos_data": [{"interfaceDescriptions": {"Vxlan1": {"lineProtocolStatus": "up", "interfaceStatus": "down"}}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Interface: Vxlan1 - Incorrect Line protocol status/Status - Expected: up/up Actual: up/down"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Interface: Vxlan1 - Incorrect Line protocol status/Status - Expected: up/up Actual: up/down"]},
},
{
"name": "failure-down-down",
"test": VerifyVxlan1Interface,
(VerifyVxlan1Interface, "failure-down-down"): {
"eos_data": [{"interfaceDescriptions": {"Vxlan1": {"lineProtocolStatus": "down", "interfaceStatus": "down"}}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Interface: Vxlan1 - Incorrect Line protocol status/Status - Expected: up/up Actual: down/down"]},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Interface: Vxlan1 - Incorrect Line protocol status/Status - Expected: up/up Actual: down/down"],
},
},
{
"name": "success",
"test": VerifyVxlanConfigSanity,
(VerifyVxlanConfigSanity, "success"): {
"eos_data": [
{
"categories": {
@ -106,14 +103,11 @@ DATA: list[dict[str, Any]] = [
},
},
"warnings": [],
},
}
],
"inputs": None,
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure",
"test": VerifyVxlanConfigSanity,
(VerifyVxlanConfigSanity, "failure"): {
"eos_data": [
{
"categories": {
@ -171,186 +165,181 @@ DATA: list[dict[str, Any]] = [
},
},
"warnings": ["Your configuration contains warnings. This does not mean misconfigurations. But you may wish to re-check your configurations."],
},
}
],
"inputs": None,
"expected": {
"result": "failure",
"messages": ["Vxlan Category: localVtep - Config sanity check is not passing"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Vxlan Category: localVtep - Config sanity check is not passing"]},
},
{
"name": "skipped",
"test": VerifyVxlanConfigSanity,
(VerifyVxlanConfigSanity, "skipped"): {
"eos_data": [{"categories": {}}],
"inputs": None,
"expected": {"result": "skipped", "messages": ["VXLAN is not configured"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["VXLAN is not configured"]},
},
{
"name": "success",
"test": VerifyVxlanVniBinding,
(VerifyVxlanVniBinding, "success"): {
"eos_data": [
{
"vxlanIntfs": {
"Vxlan1": {
"vniBindings": {
"10020": {"vlan": 20, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}},
"10020": {"vlan": 20, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}}
},
"vniBindingsToVrf": {"500": {"vrfName": "PROD", "vlan": 1199, "source": "evpn"}},
},
},
},
"vniBindingsToVrf": {"500": {"vrfName": "TEST", "vlan": 1199, "source": "evpn"}, "600": {"vrfName": "PROD", "vlan": 1198, "source": "evpn"}},
}
}
}
],
"inputs": {"bindings": {10020: 20, 500: 1199}},
"expected": {"result": "success"},
"inputs": {"bindings": {10020: 20, 500: 1199, 600: "PROD"}},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-no-binding",
"test": VerifyVxlanVniBinding,
(VerifyVxlanVniBinding, "failure-no-binding"): {
"eos_data": [
{
"vxlanIntfs": {
"Vxlan1": {
"vniBindings": {
"10020": {"vlan": 20, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}},
"10020": {"vlan": 20, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}}
},
"vniBindingsToVrf": {"500": {"vrfName": "PROD", "vlan": 1199, "source": "evpn"}},
},
},
},
}
}
}
],
"inputs": {"bindings": {10010: 10, 10020: 20, 500: 1199}},
"expected": {"result": "failure", "messages": ["Interface: Vxlan1 VNI: 10010 - Binding not found"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Interface: Vxlan1 VNI: 10010 - Binding not found"]},
},
{
"name": "failure-wrong-binding",
"test": VerifyVxlanVniBinding,
(VerifyVxlanVniBinding, "failure-vrf-wrong-binding"): {
"eos_data": [
{
"vxlanIntfs": {
"Vxlan1": {
"vniBindings": {
"10020": {"vlan": 30, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}},
"10020": {"vlan": 20, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}}
},
"vniBindingsToVrf": {"500": {"vrfName": "PROD", "vlan": 1199, "source": "evpn"}, "600": {"vrfName": "TEST", "vlan": 1199, "source": "evpn"}},
}
}
}
],
"inputs": {"bindings": {10020: 20, 500: 1199, 600: "PROD"}},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Interface: Vxlan1 VNI: 600 - Wrong VRF binding - Expected: PROD Actual: TEST"]},
},
(VerifyVxlanVniBinding, "failure-wrong-binding"): {
"eos_data": [
{
"vxlanIntfs": {
"Vxlan1": {
"vniBindings": {
"10020": {"vlan": 30, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}}
},
"vniBindingsToVrf": {"500": {"vrfName": "PROD", "vlan": 1199, "source": "evpn"}},
},
},
},
}
}
}
],
"inputs": {"bindings": {10020: 20, 500: 1199}},
"expected": {"result": "failure", "messages": ["Interface: Vxlan1 VNI: 10020 VLAN: 20 - Wrong VLAN binding - Actual: 30"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Interface: Vxlan1 VNI: 10020 - Wrong VLAN binding - Expected: 20 Actual: 30"]},
},
{
"name": "failure-no-and-wrong-binding",
"test": VerifyVxlanVniBinding,
(VerifyVxlanVniBinding, "failure-no-and-wrong-binding"): {
"eos_data": [
{
"vxlanIntfs": {
"Vxlan1": {
"vniBindings": {
"10020": {"vlan": 30, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}},
"10020": {"vlan": 30, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}}
},
"vniBindingsToVrf": {"500": {"vrfName": "PROD", "vlan": 1199, "source": "evpn"}},
},
},
},
}
}
}
],
"inputs": {"bindings": {10010: 10, 10020: 20, 500: 1199}},
"expected": {
"result": "failure",
"messages": ["Interface: Vxlan1 VNI: 10010 - Binding not found", "Interface: Vxlan1 VNI: 10020 VLAN: 20 - Wrong VLAN binding - Actual: 30"],
"result": AntaTestStatus.FAILURE,
"messages": ["Interface: Vxlan1 VNI: 10010 - Binding not found", "Interface: Vxlan1 VNI: 10020 - Wrong VLAN binding - Expected: 20 Actual: 30"],
},
},
{
"name": "skipped",
"test": VerifyVxlanVniBinding,
(VerifyVxlanVniBinding, "failure-wrong-vni-vrf-binding"): {
"eos_data": [
{
"vxlanIntfs": {
"Vxlan1": {
"vniBindings": {
"10020": {"vlan": 30, "dynamicVlan": False, "source": "static", "interfaces": {"Ethernet31": {"dot1q": 0}, "Vxlan1": {"dot1q": 20}}}
},
"vniBindingsToVrf": {"500": {"vrfName": "PROD", "vlan": 1199, "source": "evpn"}},
}
}
}
],
"inputs": {"bindings": {10020: "PROD", 500: 30}},
"expected": {
"result": AntaTestStatus.FAILURE,
"messages": ["Interface: Vxlan1 VNI: 10020 - Binding not found", "Interface: Vxlan1 VNI: 500 - Wrong VLAN binding - Expected: 30 Actual: 1199"],
},
},
(VerifyVxlanVniBinding, "skipped"): {
"eos_data": [{"vxlanIntfs": {}}],
"inputs": {"bindings": {10020: 20, 500: 1199}},
"expected": {"result": "skipped", "messages": ["Vxlan1 interface is not configured"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Interface: Vxlan1 - Not configured"]},
},
{
"name": "success",
"test": VerifyVxlanVtep,
(VerifyVxlanVtep, "success"): {
"eos_data": [{"vteps": {}, "interfaces": {"Vxlan1": {"vteps": ["10.1.1.5", "10.1.1.6"]}}}],
"inputs": {"vteps": ["10.1.1.5", "10.1.1.6"]},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "failure-missing-vtep",
"test": VerifyVxlanVtep,
(VerifyVxlanVtep, "failure-missing-vtep"): {
"eos_data": [{"vteps": {}, "interfaces": {"Vxlan1": {"vteps": ["10.1.1.5", "10.1.1.6"]}}}],
"inputs": {"vteps": ["10.1.1.5", "10.1.1.6", "10.1.1.7"]},
"expected": {"result": "failure", "messages": ["The following VTEP peer(s) are missing from the Vxlan1 interface: 10.1.1.7"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["The following VTEP peer(s) are missing from the Vxlan1 interface: 10.1.1.7"]},
},
{
"name": "failure-no-vtep",
"test": VerifyVxlanVtep,
(VerifyVxlanVtep, "failure-no-vtep"): {
"eos_data": [{"vteps": {}, "interfaces": {"Vxlan1": {"vteps": []}}}],
"inputs": {"vteps": ["10.1.1.5", "10.1.1.6"]},
"expected": {"result": "failure", "messages": ["The following VTEP peer(s) are missing from the Vxlan1 interface: 10.1.1.5, 10.1.1.6"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["The following VTEP peer(s) are missing from the Vxlan1 interface: 10.1.1.5, 10.1.1.6"]},
},
{
"name": "failure-no-input-vtep",
"test": VerifyVxlanVtep,
(VerifyVxlanVtep, "failure-no-input-vtep"): {
"eos_data": [{"vteps": {}, "interfaces": {"Vxlan1": {"vteps": ["10.1.1.5"]}}}],
"inputs": {"vteps": []},
"expected": {"result": "failure", "messages": ["Unexpected VTEP peer(s) on Vxlan1 interface: 10.1.1.5"]},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Unexpected VTEP peer(s) on Vxlan1 interface: 10.1.1.5"]},
},
{
"name": "failure-missmatch",
"test": VerifyVxlanVtep,
(VerifyVxlanVtep, "failure-missmatch"): {
"eos_data": [{"vteps": {}, "interfaces": {"Vxlan1": {"vteps": ["10.1.1.6", "10.1.1.7", "10.1.1.8"]}}}],
"inputs": {"vteps": ["10.1.1.5", "10.1.1.6"]},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"The following VTEP peer(s) are missing from the Vxlan1 interface: 10.1.1.5",
"Unexpected VTEP peer(s) on Vxlan1 interface: 10.1.1.7, 10.1.1.8",
],
},
},
{
"name": "skipped",
"test": VerifyVxlanVtep,
(VerifyVxlanVtep, "skipped"): {
"eos_data": [{"vteps": {}, "interfaces": {}}],
"inputs": {"vteps": ["10.1.1.5", "10.1.1.6", "10.1.1.7"]},
"expected": {"result": "skipped", "messages": ["Vxlan1 interface is not configured"]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Interface: Vxlan1 - Not configured"]},
},
{
"name": "success",
"test": VerifyVxlan1ConnSettings,
(VerifyVxlan1ConnSettings, "success"): {
"eos_data": [{"interfaces": {"Vxlan1": {"srcIpIntf": "Loopback1", "udpPort": 4789}}}],
"inputs": {"source_interface": "Loopback1", "udp_port": 4789},
"expected": {"result": "success"},
"expected": {"result": AntaTestStatus.SUCCESS},
},
{
"name": "skipped",
"test": VerifyVxlan1ConnSettings,
(VerifyVxlan1ConnSettings, "skipped"): {
"eos_data": [{"interfaces": {}}],
"inputs": {"source_interface": "Loopback1", "udp_port": 4789},
"expected": {"result": "skipped", "messages": ["Vxlan1 interface is not configured."]},
"expected": {"result": AntaTestStatus.SKIPPED, "messages": ["Interface: Vxlan1 - Not configured"]},
},
{
"name": "failure-wrong-interface",
"test": VerifyVxlan1ConnSettings,
(VerifyVxlan1ConnSettings, "failure-wrong-interface"): {
"eos_data": [{"interfaces": {"Vxlan1": {"srcIpIntf": "Loopback10", "udpPort": 4789}}}],
"inputs": {"source_interface": "lo1", "udp_port": 4789},
"expected": {
"result": "failure",
"messages": ["Interface: Vxlan1 - Incorrect Source interface - Expected: Loopback1 Actual: Loopback10"],
},
"expected": {"result": AntaTestStatus.FAILURE, "messages": ["Interface: Vxlan1 - Incorrect Source interface - Expected: Loopback1 Actual: Loopback10"]},
},
{
"name": "failure-wrong-port",
"test": VerifyVxlan1ConnSettings,
(VerifyVxlan1ConnSettings, "failure-wrong-port"): {
"eos_data": [{"interfaces": {"Vxlan1": {"srcIpIntf": "Loopback10", "udpPort": 4789}}}],
"inputs": {"source_interface": "Lo1", "udp_port": 4780},
"expected": {
"result": "failure",
"result": AntaTestStatus.FAILURE,
"messages": [
"Interface: Vxlan1 - Incorrect Source interface - Expected: Loopback1 Actual: Loopback10",
"Interface: Vxlan1 - Incorrect UDP port - Expected: 4780 Actual: 4789",
],
},
},
]
}

View file

@ -123,7 +123,7 @@ def click_runner(capsys: pytest.CaptureFixture[str], anta_env: dict[str, str]) -
# Patch asynceapi methods used by AsyncEOSDevice. See tests/units/test_device.py
with (
patch("asynceapi.device.Device.check_connection", return_value=True),
patch("asynceapi.device.Device.check_api_endpoint", return_value=True),
patch("asynceapi.device.Device.cli", side_effect=cli),
patch("asyncssh.connect"),
patch(

View file

@ -368,7 +368,7 @@ def test_from_ansible_overwrite(
None,
True,
False,
"`anta get tests --module <module>` does not support relative imports",
"`--module <module>` option does not support relative imports",
ExitCode.USAGE_ERROR,
id="Use relative module name",
),
@ -454,3 +454,185 @@ def test_get_tests_local_module(click_runner: CliRunner) -> None:
if cwd != local_module_parent_path:
assert "injecting CWD in PYTHONPATH and retrying..." in result.output
assert "No test found in 'local_module'" in result.output
@pytest.mark.parametrize(
("module", "test_name", "catalog", "expected_output", "expected_exit_code"),
[
pytest.param(
None,
None,
None,
"VerifyAcctConsoleMethods",
ExitCode.OK,
id="Get all commands",
),
pytest.param(
"anta.tests.aaa",
None,
None,
"VerifyAcctConsoleMethods",
ExitCode.OK,
id="Get commands, filter on module",
),
pytest.param(
None,
"VerifyNTPAssociations",
None,
"VerifyNTPAssociations",
ExitCode.OK,
id="Get commands, filter on exact test name",
),
pytest.param(
None,
"VerifyNTP",
None,
"anta.tests.system",
ExitCode.OK,
id="Get commands, filter on included test name",
),
pytest.param(
"unknown_module",
None,
None,
"Module `unknown_module` was not found!",
ExitCode.USAGE_ERROR,
id="Get commands wrong module",
),
pytest.param(
"unknown_module.unknown",
None,
None,
"Module `unknown_module.unknown` was not found!",
ExitCode.USAGE_ERROR,
id="Get commands wrong submodule",
),
pytest.param(
".unknown_module",
None,
None,
"`--module <module>` option does not support relative imports",
ExitCode.USAGE_ERROR,
id="Use relative module name",
),
pytest.param(
None,
"VerifySomething",
None,
"No test 'VerifySomething' found in 'anta.tests'",
ExitCode.OK,
id="Get commands wrong test name",
),
pytest.param(
"anta.tests.aaa",
"VerifyNTP",
None,
"No test 'VerifyNTP' found in 'anta.tests.aaa'",
ExitCode.OK,
id="Get commands test exists but not in module",
),
pytest.param(
None,
None,
DATA_DIR / "test_catalog.yml",
"VerifyEOSVersion",
ExitCode.OK,
id="Get all commands from catalog",
),
pytest.param(
"anta.tests.aaa",
None,
DATA_DIR / "test_catalog.yml",
"No test found in 'anta.tests.aaa' for catalog '", # partial match as no test from aaa in the catalog
ExitCode.OK,
id="Get all commands from module in catalog",
),
pytest.param(
None,
None,
DATA_DIR / "non_existing_catalog.yml",
"Invalid value for '--catalog'",
ExitCode.USAGE_ERROR,
id="Catalog does not exist",
),
# TODO: catalog format JSON
],
)
def test_get_commands(
click_runner: CliRunner, module: str | None, test_name: str | None, catalog: str | None, expected_output: str, expected_exit_code: str
) -> None:
"""Test `anta get commands`."""
cli_args = [
"get",
"commands",
]
if module is not None:
cli_args.extend(["--module", module])
if test_name is not None:
cli_args.extend(["--test", test_name])
if catalog is not None:
cli_args.extend(["--catalog", catalog])
# Make sure to disable any ANTA_CATALOG env that could be set and pollute the test
result = click_runner.invoke(anta, cli_args, env={"ANTA_CATALOG": None})
assert result.exit_code == expected_exit_code
assert expected_output in result.output
@pytest.mark.parametrize(
("module", "test_name", "catalog", "expected_count"),
[
pytest.param(
"anta.tests.aaa",
None,
None,
4,
id="Get unique commands, filter on module",
),
pytest.param(
None,
"VerifyNTPAssociations",
None,
1,
id="Get unique commands, filter on exact test name",
),
pytest.param(
None,
"VerifyNTP",
None,
2,
id="Get unique commands, filter on included test name",
),
pytest.param(
None,
None,
DATA_DIR / "test_catalog.yml",
1,
id="Get all unique commands from catalog",
),
],
)
def test_get_commands_unique(click_runner: CliRunner, module: str | None, test_name: str | None, catalog: str | None, expected_count: int) -> None:
"""Test `anta get commands`."""
cli_args = [
"get",
"commands",
]
if module is not None:
cli_args.extend(["--module", module])
if test_name is not None:
cli_args.extend(["--test", test_name])
if catalog is not None:
cli_args.extend(["--catalog", catalog])
cli_args.extend(["--unique"])
# Make sure to disable any ANTA_CATALOG env that could be set and pollute the test
result = click_runner.invoke(anta, cli_args, env={"ANTA_CATALOG": None})
assert expected_count == len(result.output.splitlines())

View file

@ -13,7 +13,7 @@ from unittest.mock import MagicMock, patch
import pytest
import requests
from anta.cli.get.utils import create_inventory_from_ansible, create_inventory_from_cvp, extract_examples, find_tests_examples, get_cv_token, print_test
from anta.cli.get.utils import create_inventory_from_ansible, create_inventory_from_cvp, extract_examples, find_tests_in_module, get_cv_token, print_test
from anta.inventory import AntaInventory
from anta.models import AntaCommand, AntaTemplate, AntaTest
@ -219,14 +219,14 @@ class TypoExampleTest(AntaTest):
self.result.is_success()
def test_find_tests_examples() -> None:
def test_find_tests_in_module() -> None:
"""Test find_tests_examples.
Only testing the failure scenarii not tested through test_commands.
TODO: expand
"""
with pytest.raises(ValueError, match="Error when importing"):
find_tests_examples("blah", "UnusedTestName")
find_tests_in_module("blah", "UnusedTestName")
def test_print_test() -> None:

View file

@ -178,7 +178,7 @@ def test_anta_nrfu_md_report(click_runner: CliRunner, tmp_path: Path) -> None:
def test_anta_nrfu_md_report_failure(click_runner: CliRunner, tmp_path: Path) -> None:
"""Test anta nrfu md-report failure."""
md_output = tmp_path / "test.md"
with patch("anta.reporter.md_reporter.MDReportGenerator.generate", side_effect=OSError()):
with patch("anta.reporter.md_reporter.MDReportGenerator.generate_sections", side_effect=OSError()):
result = click_runner.invoke(anta, ["nrfu", "md-report", "--md-output", str(md_output)])
assert result.exit_code == ExitCode.USAGE_ERROR
@ -206,5 +206,17 @@ def test_anta_nrfu_md_report_with_hide(click_runner: CliRunner, tmp_path: Path)
total_tests = int(match.group(1))
total_tests_success = int(match.group(2))
assert total_tests == 0
assert total_tests_success == 0
assert total_tests == 3
assert total_tests_success == 3
# Collecting the rows inside the Test Results section
row_count = 0
lines = content.splitlines()
idx = lines.index("## Test Results")
for line in lines[idx + 1 :]:
if line.startswith("|") and "---" not in line:
row_count += 1
# Reducing the row count by 1, as above conditions counts the TABLE_HEADING
assert (row_count - 1) == 0

View file

@ -6,35 +6,21 @@
from __future__ import annotations
import sys
from importlib import reload
from typing import TYPE_CHECKING, Any
from typing import Any
from unittest.mock import patch
import pytest
import anta.cli
if TYPE_CHECKING:
from types import ModuleType
builtins_import = __import__
# Tried to achieve this with mock
# http://materials-scientist.com/blog/2021/02/11/mocking-failing-module-import-python/
def import_mock(name: str, *args: Any) -> ModuleType: # noqa: ANN401
"""Mock."""
if name == "click":
msg = "No module named 'click'"
raise ModuleNotFoundError(msg)
return builtins_import(name, *args)
def test_cli_error_missing(capsys: pytest.CaptureFixture[Any]) -> None:
# https://github.com/python/cpython/issues/88852
@pytest.mark.skipif(sys.version_info <= (3, 11), reason="Unreliable behavior patching sys.modules before 3.11")
def test_cli_error_missing_click(capsys: pytest.CaptureFixture[Any]) -> None:
"""Test ANTA errors out when anta[cli] was not installed."""
with patch.dict(sys.modules) as sys_modules, patch("builtins.__import__", import_mock):
del sys_modules["anta.cli._main"]
reload(anta.cli)
with patch.dict(sys.modules, {"click": None}) as sys_modules:
for k in list(sys_modules.keys()):
if k.startswith("anta."):
del sys_modules[k]
import anta.cli
with pytest.raises(SystemExit) as e_info:
anta.cli.cli()
@ -53,3 +39,18 @@ def test_cli_error_missing(capsys: pytest.CaptureFixture[Any]) -> None:
assert "Make sure you've installed everything with: pip install 'anta[cli]'" in captured.out
assert "The caught exception was:" in captured.out
assert e_info.value.code == 1
# https://github.com/python/cpython/issues/88852
@pytest.mark.skipif(sys.version_info <= (3, 11), reason="Unreliable behavior patching sys.modules before 3.11")
def test_cli_error_missing_other() -> None:
"""Test ANTA errors out when anta[cli] was not installed."""
with patch.dict(sys.modules, {"httpx": None}) as sys_modules:
# Need to clean up from previous runs a path that will trigger reimporting httpx
for k in list(sys_modules.keys()):
if k.startswith("anta."):
del sys_modules[k]
import anta.cli
with pytest.raises(ImportError, match="httpx"):
anta.cli.cli()

View file

@ -5,8 +5,10 @@
from __future__ import annotations
import os
from pathlib import Path
from typing import TYPE_CHECKING, Any
from unittest import mock
from unittest.mock import patch
import pytest
@ -15,7 +17,7 @@ import yaml
from anta.device import AntaDevice, AsyncEOSDevice
if TYPE_CHECKING:
from collections.abc import Iterator
from collections.abc import Generator, Iterator
from anta.models import AntaCommand
@ -83,3 +85,10 @@ def yaml_file(request: pytest.FixtureRequest, tmp_path: Path) -> Path:
content: dict[str, Any] = request.param
file.write_text(yaml.dump(content, allow_unicode=True))
return file
@pytest.fixture
def setenvvar(monkeypatch: pytest.MonkeyPatch) -> Generator[pytest.MonkeyPatch, None, None]:
"""Fixture to set environment variables for testing."""
with mock.patch.dict(os.environ, clear=True):
yield monkeypatch

View file

@ -28,7 +28,9 @@ from anta.tests.routing.bgp import (
)
if TYPE_CHECKING:
from anta.custom_types import Afi, RedistributedAfiSafi, RedistributedProtocol, Safi
from ipaddress import IPv4Address, IPv6Address
from anta.custom_types import Afi, Interface, RedistributedAfiSafi, RedistributedProtocol, Safi
class TestBgpAddressFamily:
@ -60,6 +62,33 @@ class TestBgpAddressFamily:
BgpAddressFamily(afi=afi, safi=safi, vrf=vrf)
class TestBgpPeer:
"""Test anta.input_models.routing.bgp.BgpPeer."""
@pytest.mark.parametrize(
("peer_address", "interface"),
[
pytest.param("10.1.1.0", None, id="peer"),
pytest.param(None, "Et1", id="interface"),
],
)
def test_valid(self, peer_address: IPv4Address | IPv6Address, interface: Interface) -> None:
"""Test BgpPeer valid inputs."""
BgpPeer(peer_address=peer_address, interface=interface)
@pytest.mark.parametrize(
("peer_address", "interface"),
[
pytest.param("10.1.1.0", "Et1", id="both"),
pytest.param(None, "None", id="both-None"),
],
)
def test_invalid(self, peer_address: IPv4Address | IPv6Address, interface: Interface) -> None:
"""Test BgpPeer invalid inputs."""
with pytest.raises(ValidationError):
BgpPeer(peer_address=peer_address, interface=interface)
class TestVerifyBGPPeerCountInput:
"""Test anta.tests.routing.bgp.VerifyBGPPeerCount.Input."""

View file

@ -19,6 +19,9 @@ if TYPE_CHECKING:
from _pytest.mark.structures import ParameterSet
from anta.device import AntaDevice, AsyncEOSDevice
INIT_VALID_PARAMS: list[ParameterSet] = [
pytest.param(
{"anta_inventory": {"hosts": [{"host": "192.168.0.17"}, {"host": "192.168.0.2"}, {"host": "my.awesome.host.com"}]}},
@ -87,3 +90,22 @@ class TestAntaInventory:
with pytest.raises(OSError, match="No such file or directory"):
_ = AntaInventory.parse(filename="dummy.yml", username="arista", password="arista123")
assert "Unable to parse ANTA Device Inventory file" in caplog.records[0].message
@pytest.mark.parametrize(("inventory"), [{"count": 3}], indirect=True)
def test_max_potential_connections(self, inventory: AntaInventory) -> None:
"""Test max_potential_connections property with regular AsyncEOSDevice objects in the inventory."""
# Each AsyncEOSDevice has a max_connections of 100
assert inventory.max_potential_connections == 300
@pytest.mark.parametrize(("device"), [{"name": "anta_device"}], indirect=True)
def test_get_potential_connections_custom_anta_device(self, caplog: pytest.LogCaptureFixture, async_device: AsyncEOSDevice, device: AntaDevice) -> None:
"""Test max_potential_connections property with an AntaDevice with no max_connections in the inventory."""
caplog.set_level(logging.DEBUG)
inventory = AntaInventory()
inventory.add_device(async_device)
inventory.add_device(device)
assert len(inventory) == 2
assert inventory.max_potential_connections is None
assert "Device anta_device 'max_connections' is not available" in caplog.messages

View file

@ -7,15 +7,48 @@ from __future__ import annotations
from io import StringIO
from pathlib import Path
from typing import TYPE_CHECKING, ClassVar
import pytest
from anta.reporter.md_reporter import MDReportBase, MDReportGenerator
from anta.result_manager import ResultManager
from anta.result_manager.models import AntaTestStatus
from anta.tools import convert_categories
if TYPE_CHECKING:
from collections.abc import Generator
DATA_DIR: Path = Path(__file__).parent.parent.parent.resolve() / "data"
class FailedTestResultsSummary(MDReportBase):
"""Test-only class used for simulating behavior in unit tests.
Generates the `## Failed Test Results Summary` section of the markdown report.
"""
TABLE_HEADING: ClassVar[list[str]] = [
"| Device Under Test | Categories | Test | Description | Custom Field | Result | Messages |",
"| ----------------- | ---------- | ---- | ----------- | ------------ | ------ | -------- |",
]
def generate_rows(self) -> Generator[str, None, None]:
"""Generate the rows of the all test results table."""
for result in self.results.results:
messages = self.safe_markdown(result.messages[0]) if len(result.messages) == 1 else self.safe_markdown("<br>".join(result.messages))
categories = ", ".join(sorted(convert_categories(result.categories)))
yield (
f"| {result.name or '-'} | {categories or '-'} | {result.test or '-'} "
f"| {result.description or '-'} | {self.safe_markdown(result.custom_field) or '-'} | {result.result or '-'} | {messages or '-'} |\n"
)
def generate_section(self) -> None:
"""Generate the `## Failed Test Results Summary` section of the markdown report."""
self.write_heading(heading_level=2)
self.write_table(table_heading=self.TABLE_HEADING)
def test_md_report_generate(tmp_path: Path, result_manager: ResultManager) -> None:
"""Test the MDReportGenerator class."""
md_filename = tmp_path / "test.md"
@ -35,6 +68,31 @@ def test_md_report_generate(tmp_path: Path, result_manager: ResultManager) -> No
assert content == expected_content
def test_md_report_generate_sections(tmp_path: Path, result_manager: ResultManager) -> None:
"""Test the MDReportGenerator class."""
md_filename = tmp_path / "test.md"
expected_report = "test_md_report_custom_sections.md"
rm = result_manager.sort(sort_by=["name", "categories", "test"])
sections = [(section, rm) for section in MDReportGenerator.DEFAULT_SECTIONS]
# Adding custom section
failed_section = (FailedTestResultsSummary, rm.filter({AntaTestStatus.SUCCESS, AntaTestStatus.ERROR, AntaTestStatus.SKIPPED, AntaTestStatus.UNSET}))
sections.insert(-1, failed_section)
# Generate the Markdown report
MDReportGenerator.generate_sections(sections, md_filename)
assert md_filename.exists()
# Load the existing Markdown report to compare with the generated one
with (DATA_DIR / expected_report).open("r", encoding="utf-8") as f:
expected_content = f.read()
# Check the content of the Markdown file
content = md_filename.read_text(encoding="utf-8")
assert content == expected_content
def test_md_report_base() -> None:
"""Test the MDReportBase class."""
@ -52,3 +110,17 @@ def test_md_report_base() -> None:
with pytest.raises(NotImplementedError, match="Subclasses should implement this method"):
report.generate_rows()
def test_md_report_error(result_manager: ResultManager) -> None:
"""Test the MDReportGenerator class to OSError to be raised."""
md_filename = Path("non_existent_directory/non_existent_file.md")
rm = result_manager.sort(sort_by=["name", "categories", "test"])
sections = [(section, rm) for section in MDReportGenerator.DEFAULT_SECTIONS]
with pytest.raises(OSError, match="No such file or directory"):
MDReportGenerator.generate_sections(sections, md_filename)
with pytest.raises(OSError, match="No such file or directory"):
MDReportGenerator.generate(result_manager, md_filename)

View file

@ -172,11 +172,11 @@ class TestResultManager:
assert "results_by_status" not in result_manager.__dict__
# Access the cache
assert result_manager.get_total_results() == 30
assert result_manager.get_total_results() == 181
# Check the cache is filled with the correct results count
assert "results_by_status" in result_manager.__dict__
assert sum(len(v) for v in result_manager.__dict__["results_by_status"].values()) == 30
assert sum(len(v) for v in result_manager.__dict__["results_by_status"].values()) == 181
# Add a new test
result_manager.add(result=test_result_factory())
@ -185,46 +185,51 @@ class TestResultManager:
assert "results_by_status" not in result_manager.__dict__
# Access the cache again
assert result_manager.get_total_results() == 31
assert result_manager.get_total_results() == 182
# Check the cache is filled again with the correct results count
assert "results_by_status" in result_manager.__dict__
assert sum(len(v) for v in result_manager.__dict__["results_by_status"].values()) == 31
assert sum(len(v) for v in result_manager.__dict__["results_by_status"].values()) == 182
def test_get_results(self, result_manager: ResultManager) -> None:
"""Test ResultManager.get_results."""
# Check for single status
success_results = result_manager.get_results(status={AntaTestStatus.SUCCESS})
assert len(success_results) == 4
assert len(success_results) == 43
assert all(r.result == "success" for r in success_results)
# Check for multiple statuses
failure_results = result_manager.get_results(status={AntaTestStatus.FAILURE, AntaTestStatus.ERROR})
assert len(failure_results) == 17
assert len(failure_results) == 104
assert all(r.result in {"failure", "error"} for r in failure_results)
# Check all results
all_results = result_manager.get_results()
assert len(all_results) == 30
assert len(all_results) == 181
def test_get_results_sort_by(self, result_manager: ResultManager) -> None:
"""Test ResultManager.get_results with sort_by."""
# Check all results with sort_by result
all_results = result_manager.get_results(sort_by=["result"])
assert len(all_results) == 30
assert [r.result for r in all_results] == ["error"] * 2 + ["failure"] * 15 + ["skipped"] * 9 + ["success"] * 4
assert len(all_results) == 181
assert [r.result for r in all_results] == ["error"] * 1 + ["failure"] * 103 + ["skipped"] * 34 + ["success"] * 43
# Check all results with sort_by device (name)
all_results = result_manager.get_results(sort_by=["name"])
assert len(all_results) == 30
assert len(all_results) == 181
assert all_results[0].name == "s1-spine1"
# Check multiple statuses with sort_by categories
success_skipped_results = result_manager.get_results(status={AntaTestStatus.SUCCESS, AntaTestStatus.SKIPPED}, sort_by=["categories"])
assert len(success_skipped_results) == 13
assert len(success_skipped_results) == 77
assert success_skipped_results[0].categories == ["avt"]
assert success_skipped_results[-1].categories == ["vxlan"]
# Check multiple statuses with sort_by custom_field
success_skipped_results = result_manager.get_results(status={AntaTestStatus.SUCCESS, AntaTestStatus.SKIPPED}, sort_by=["custom_field"])
assert len(success_skipped_results) == 77
assert success_skipped_results[-1].test == "VerifyReloadCause"
# Check all results with bad sort_by
with pytest.raises(
ValueError,
@ -237,18 +242,18 @@ class TestResultManager:
def test_get_total_results(self, result_manager: ResultManager) -> None:
"""Test ResultManager.get_total_results."""
# Test all results
assert result_manager.get_total_results() == 30
assert result_manager.get_total_results() == 181
# Test single status
assert result_manager.get_total_results(status={AntaTestStatus.SUCCESS}) == 4
assert result_manager.get_total_results(status={AntaTestStatus.FAILURE}) == 15
assert result_manager.get_total_results(status={AntaTestStatus.ERROR}) == 2
assert result_manager.get_total_results(status={AntaTestStatus.SKIPPED}) == 9
assert result_manager.get_total_results(status={AntaTestStatus.SUCCESS}) == 43
assert result_manager.get_total_results(status={AntaTestStatus.FAILURE}) == 103
assert result_manager.get_total_results(status={AntaTestStatus.ERROR}) == 1
assert result_manager.get_total_results(status={AntaTestStatus.SKIPPED}) == 34
# Test multiple statuses
assert result_manager.get_total_results(status={AntaTestStatus.SUCCESS, AntaTestStatus.FAILURE}) == 19
assert result_manager.get_total_results(status={AntaTestStatus.SUCCESS, AntaTestStatus.FAILURE, AntaTestStatus.ERROR}) == 21
assert result_manager.get_total_results(status={AntaTestStatus.SUCCESS, AntaTestStatus.FAILURE, AntaTestStatus.ERROR, AntaTestStatus.SKIPPED}) == 30
assert result_manager.get_total_results(status={AntaTestStatus.SUCCESS, AntaTestStatus.FAILURE}) == 146
assert result_manager.get_total_results(status={AntaTestStatus.SUCCESS, AntaTestStatus.FAILURE, AntaTestStatus.ERROR}) == 147
assert result_manager.get_total_results(status={AntaTestStatus.SUCCESS, AntaTestStatus.FAILURE, AntaTestStatus.ERROR, AntaTestStatus.SKIPPED}) == 181
@pytest.mark.parametrize(
("status", "error_status", "ignore_error", "expected_status"),
@ -546,6 +551,32 @@ class TestResultManager:
assert results[2].result == "failure"
assert results[2].test == "Test2"
def test_sort_fields_as_none(self, test_result_factory: Callable[[], TestResult]) -> None:
"""Test sorting by multiple fields."""
result_manager = ResultManager()
test1 = test_result_factory()
test1.result = AntaTestStatus.ERROR
test1.test = "Test3"
test1.custom_field = "custom"
test2 = test_result_factory()
test2.result = AntaTestStatus.ERROR
test2.test = "Test1"
test3 = test_result_factory()
test3.result = AntaTestStatus.FAILURE
test3.test = "Test2"
result_manager.results = [test1, test2, test3]
sorted_manager = result_manager.sort(["custom_field"])
results = sorted_manager.results
assert results[0].result == "error"
assert results[0].test == "Test1"
assert results[1].result == "failure"
assert results[1].test == "Test2"
assert results[2].result == "error"
assert results[2].test == "Test3"
assert results[2].custom_field == "custom"
def test_sort_invalid_field(self) -> None:
"""Test that sort method raises ValueError for invalid sort_by fields."""
result_manager = ResultManager()
@ -561,3 +592,34 @@ class TestResultManager:
"""Test that the sort method is chainable."""
result_manager = ResultManager()
assert isinstance(result_manager.sort(["name"]), ResultManager)
def test_merge_result_manager(self) -> None:
"""Test the merge_results function."""
result = ResultManager()
final_merged_results = ResultManager.merge_results([result])
assert isinstance(final_merged_results, ResultManager)
def test_merge_two_result_managers(self, test_result_factory: Callable[[], TestResult]) -> None:
"""Test merging two non-empty ResultManager instances."""
rm1 = ResultManager()
test1_rm1 = test_result_factory()
test1_rm1.name = "device1"
rm1.add(test1_rm1)
test2_rm1 = test_result_factory()
test2_rm1.name = "device2"
rm1.add(test2_rm1)
rm2 = ResultManager()
test1_rm2 = test_result_factory()
test1_rm2.name = "device3"
rm2.add(test1_rm2)
merged_rm = ResultManager.merge_results([rm1, rm2])
assert len(merged_rm) == 3
assert {r.name for r in merged_rm.results} == {"device1", "device2", "device3"}
def test_merge_empty_list(self) -> None:
"""Test merging an empty list of ResultManager instances."""
merged_rm = ResultManager.merge_results([])
assert isinstance(merged_rm, ResultManager)
assert len(merged_rm) == 0

414
tests/units/test__runner.py Normal file
View file

@ -0,0 +1,414 @@
# 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.
"""Test anta._runner.py."""
from __future__ import annotations
import logging
import os
from collections import defaultdict
from pathlib import Path
from typing import ClassVar
import pytest
import respx
from pydantic import ValidationError
from anta._runner import AntaRunContext, AntaRunFilters, AntaRunner
from anta.catalog import AntaCatalog, AntaTestDefinition
from anta.inventory import AntaInventory
from anta.models import AntaCommand, AntaTemplate, AntaTest
from anta.result_manager import ResultManager
from anta.result_manager.models import TestResult as AntaTestResult
from anta.settings import DEFAULT_MAX_CONCURRENCY, DEFAULT_NOFILE, AntaRunnerSettings
from anta.tests.routing.generic import VerifyRoutingTableEntry
DATA_DIR: Path = Path(__file__).parent.parent.resolve() / "data"
class TestAntaRunner:
"""Test AntaRunner class."""
def test_init_with_default_settings(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test initialization with default settings."""
caplog.set_level(logging.DEBUG)
default_settings = {"nofile": DEFAULT_NOFILE, "max_concurrency": DEFAULT_MAX_CONCURRENCY}
runner = AntaRunner()
assert f"AntaRunner initialized with settings: {default_settings}" in caplog.messages
assert runner._settings
def test_init_with_custom_env_settings(self, caplog: pytest.LogCaptureFixture, setenvvar: pytest.MonkeyPatch) -> None:
"""Test initialization with custom env settings."""
caplog.set_level(logging.DEBUG)
desired_settings = {"nofile": 1048576, "max_concurrency": 10000}
setenvvar.setenv("ANTA_NOFILE", str(desired_settings["nofile"]))
setenvvar.setenv("ANTA_MAX_CONCURRENCY", str(desired_settings["max_concurrency"]))
runner = AntaRunner()
assert f"AntaRunner initialized with settings: {desired_settings}" in caplog.messages
assert runner._settings
def test_init_with_provided_settings(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test initialization with provided settings."""
caplog.set_level(logging.DEBUG)
desired_settings = AntaRunnerSettings(nofile=1048576, max_concurrency=10000)
runner = AntaRunner(settings=desired_settings)
assert f"AntaRunner initialized with settings: {desired_settings.model_dump()}" in caplog.messages
assert runner._settings
async def test_dry_run(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test AntaRunner.run() in dry-run."""
caplog.set_level(logging.INFO)
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
runner = AntaRunner()
ctx = await runner.run(inventory, catalog, dry_run=True)
# Validate the final context attributes
assert ctx.selected_inventory == ctx.inventory == inventory
assert len(ctx.manager) > 0
assert ctx.manager.status == "unset"
assert ctx.total_tests_scheduled > 0
assert ctx.total_devices_filtered_by_tags == 0
assert ctx.total_devices_unreachable == 0
assert ctx.total_devices_selected_for_testing == ctx.total_devices_in_inventory == len(inventory)
assert ctx.duration is not None
assert "Dry-run mode, exiting before running the tests." in caplog.messages
@pytest.mark.parametrize(
("filters", "expected_devices", "expected_tests"),
[
pytest.param(
AntaRunFilters(devices=None, tests=None, tags=None),
3,
27,
id="all-tests",
),
pytest.param(
AntaRunFilters(devices=None, tests=None, tags={"leaf"}),
2,
6,
id="1-tag",
),
pytest.param(
AntaRunFilters(devices=None, tests=None, tags={"leaf", "spine"}),
3,
9,
id="2-tags",
),
pytest.param(
AntaRunFilters(devices=None, tests={"VerifyMlagStatus", "VerifyUptime"}, tags=None),
3,
5,
id="filtered-tests",
),
pytest.param(
AntaRunFilters(devices=None, tests={"VerifyMlagStatus", "VerifyUptime"}, tags={"leaf"}),
2,
4,
id="1-tag-filtered-tests",
),
pytest.param(
AntaRunFilters(devices={"leaf1"}, tests=None, tags=None),
1,
9,
id="filtered-devices",
),
pytest.param(
AntaRunFilters(devices=None, tests=None, tags={"invalid"}),
0,
0,
id="invalid-tag",
),
pytest.param(
AntaRunFilters(devices={"invalid"}, tests=None, tags=None),
0,
0,
id="invalid-device",
),
pytest.param(
AntaRunFilters(devices=None, tests={"invalid"}, tags=None),
3,
0,
id="invalid-test",
),
pytest.param(
AntaRunFilters(devices=None, tests=None, tags={"dc1"}),
1,
0,
id="device-tag-no-tests",
),
],
)
async def test_run_filters(self, caplog: pytest.LogCaptureFixture, filters: AntaRunFilters, expected_devices: int, expected_tests: int) -> None:
"""Test AntaRunner.run() with different filters."""
caplog.set_level(logging.WARNING)
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
runner = AntaRunner()
ctx = await runner.run(inventory, catalog, filters=filters, dry_run=True)
# Gather the warning message
warning_msg = None
if expected_devices == 0:
msg = "The inventory is empty after filtering by tags/devices. "
if filters.devices:
msg += f"Devices filter: {', '.join(sorted(filters.devices))}. "
if filters.tags:
msg += f"Tags filter: {', '.join(sorted(filters.tags))}. "
msg += "Exiting ..."
elif expected_tests == 0:
msg = "No tests scheduled to run after filtering by tags/tests. "
if filters.tests:
msg += f"Tests filter: {', '.join(sorted(filters.tests))}. "
if filters.tags:
msg += f"Tags filter: {', '.join(sorted(filters.tags))}. "
msg += "Exiting ..."
if warning_msg is not None:
assert msg in ctx.warnings_at_setup
assert msg in caplog.messages
assert ctx.total_tests_scheduled == expected_tests
assert ctx.total_devices_selected_for_testing == expected_devices
async def test_run_invalid_filters(self) -> None:
"""Test AntaRunner.run() with invalid filters."""
inventory = AntaInventory()
catalog = AntaCatalog()
runner = AntaRunner()
with pytest.raises(ValidationError, match="1 validation error for AntaRunFilters"):
await runner.run(inventory, catalog, filters=AntaRunFilters(devices="invalid"), dry_run=True) # type: ignore[arg-type]
async def test_run_provided_manager(self) -> None:
"""Test AntaRunner.run() with a provided ResultManager instance."""
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
manager = ResultManager()
runner = AntaRunner()
ctx = await runner.run(inventory, catalog, manager, dry_run=True)
assert isinstance(ctx.manager, ResultManager)
assert ctx.manager is manager
assert len(manager) == 27
async def test_run_provided_manager_not_empty(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test AntaRunner.run() with a provided non-empty ResultManager instance."""
caplog.set_level(logging.WARNING)
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
manager = ResultManager()
test = AntaTestResult(name="DC1-LEAF1A", test="VerifyNTP", categories=["system"], description="NTP Test")
runner = AntaRunner()
manager.add(test)
ctx = await runner.run(inventory, catalog, manager, dry_run=True)
assert isinstance(ctx.manager, ResultManager)
assert ctx.manager is manager
assert len(manager) == 28
assert len(manager.device_stats) == ctx.total_devices_selected_for_testing + 1
warning_msg = (
"Appending new results to the provided ResultManager which already holds 1 results. Statistics in this run context are for the current execution only."
)
assert warning_msg in ctx.warnings_at_setup
assert warning_msg in caplog.messages
async def test_run_empty_catalog(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test AntaRunner.run() with an empty AntaCatalog."""
caplog.set_level(logging.WARNING)
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
catalog = AntaCatalog()
runner = AntaRunner()
ctx = await runner.run(inventory, catalog)
warning_msg = "The list of tests is empty. Exiting ..."
assert warning_msg in ctx.warnings_at_setup
assert warning_msg in caplog.messages
async def test_run_empty_inventory(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test AntaRunner.run() with an empty AntaInventory."""
caplog.set_level(logging.WARNING)
inventory = AntaInventory()
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
runner = AntaRunner()
ctx = await runner.run(inventory, catalog)
warning_msg = "The initial inventory is empty. Exiting ..."
assert warning_msg in ctx.warnings_at_setup
assert warning_msg in caplog.messages
@pytest.mark.parametrize("inventory", [{"reachable": False}], indirect=True)
async def test_run_no_reachable_devices(self, caplog: pytest.LogCaptureFixture, inventory: AntaInventory) -> None:
"""Test AntaRunner.run() with an empty AntaInventory."""
caplog.set_level(logging.WARNING)
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
runner = AntaRunner()
ctx = await runner.run(inventory, catalog)
assert ctx.total_devices_unreachable == ctx.total_devices_in_inventory
assert "device-0" in ctx.devices_unreachable_at_setup
warning_msg = "No reachable devices found for testing after connectivity checks. Exiting ..."
assert warning_msg in ctx.warnings_at_setup
assert warning_msg in caplog.messages
async def test_run_invalid_anta_test(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test AntaRunner.run() with a provided non-empty ResultManager instance."""
caplog.set_level(logging.CRITICAL)
class InvalidTest(AntaTest):
"""ANTA test that raises an exception when test is called."""
categories: ClassVar[list[str]] = []
commands: ClassVar[list[AntaCommand | AntaTemplate]] = []
def test(self) -> None: # type: ignore[override]
"""Test function."""
msg = "Test not implemented"
raise NotImplementedError(msg)
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
test_definition = AntaTestDefinition(test=InvalidTest, inputs=None)
catalog = AntaCatalog(tests=[test_definition])
runner = AntaRunner()
ctx = await runner.run(inventory, catalog, dry_run=True)
assert len(ctx.manager) == 0
error_msg = (
f"There is an error when creating test {__name__}.InvalidTest.\n"
"If this is not a custom test implementation: "
"Please reach out to the maintainer team or open an issue on Github: https://github.com/aristanetworks/anta.\n"
"NotImplementedError: Test not implemented"
)
assert error_msg in caplog.messages
async def test_log_run_information_default(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test AntaRunner._log_run_information with default settings."""
caplog.set_level(logging.INFO)
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
runner = AntaRunner()
await runner.run(inventory, catalog, dry_run=True)
expected_output = [
"ANTA NRFU Dry Run Information",
"Devices:",
" Total in initial inventory: 3",
" Selected for testing: 3",
"Total number of selected tests: 27",
]
for line in expected_output:
assert line in caplog.text
async def test_log_run_information_concurrency_limit(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test AntaRunner._log_run_information with higher tests count than concurrency limit."""
caplog.set_level(logging.WARNING)
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
runner_settings = AntaRunnerSettings(max_concurrency=20)
runner = AntaRunner(settings=runner_settings)
ctx = await runner.run(inventory, catalog, dry_run=True)
warning_msg = "Tests count (27) exceeds concurrent limit (20). Tests will be throttled. Please consult the ANTA FAQ."
assert warning_msg in ctx.warnings_at_setup
assert warning_msg in caplog.messages
@pytest.mark.skipif(os.name != "posix", reason="Very unlikely to happen on non-POSIX systems due to sys.maxsize")
async def test_log_run_information_file_descriptor_limit(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test AntaRunner._log_run_information with higher connections count than file descriptor limit."""
caplog.set_level(logging.WARNING)
inventory = AntaInventory.parse(filename=DATA_DIR / "test_inventory_with_tags.yml", username="anta", password="anta")
catalog = AntaCatalog.parse(filename=DATA_DIR / "test_catalog_with_tags.yml")
runner_settings = AntaRunnerSettings(nofile=128)
runner = AntaRunner(settings=runner_settings)
ctx = await runner.run(inventory, catalog, dry_run=True)
warning_msg = "Potential connections (300) exceeds file descriptor limit (128). Connection errors may occur. Please consult the ANTA FAQ."
assert warning_msg in ctx.warnings_at_setup
assert warning_msg in caplog.messages
@pytest.mark.parametrize(("inventory"), [{"count": 3}], indirect=True)
@respx.mock
async def test_run(self, inventory: AntaInventory) -> None:
"""Test AntaRunner.run()."""
# Mock the eAPI requests
respx.post(path="/command-api", headers={"Content-Type": "application/json-rpc"}, json__params__cmds__0__cmd="show ip route vrf default").respond(
json={"result": [{"vrfs": {"default": {"routes": {}}}}]}
)
tests = [AntaTestDefinition(test=VerifyRoutingTableEntry, inputs={"routes": [f"10.1.0.{i}"], "collect": "all"}) for i in range(5)]
catalog = AntaCatalog(tests=tests)
runner = AntaRunner()
ctx = await runner.run(inventory, catalog)
assert ctx.total_devices_selected_for_testing == 3
assert ctx.total_tests_scheduled == 15
assert len(ctx.warnings_at_setup) == 0
assert len(ctx.manager) == 15
for result in ctx.manager.results:
assert result.result == "failure"
# pylint: disable=too-few-public-methods
class TestAntaRunContext:
"""Test AntaRunContext class."""
def test_init(self) -> None:
"""Test initialization."""
inventory = AntaInventory()
catalog = AntaCatalog()
manager = ResultManager()
filters = AntaRunFilters()
ctx = AntaRunContext(inventory, catalog, manager, filters)
# Test initialized attributes
assert ctx.inventory is inventory
assert ctx.catalog is catalog
assert ctx.manager is manager
assert ctx.filters is filters
assert not ctx.dry_run
assert isinstance(ctx.selected_inventory, AntaInventory)
assert len(ctx.selected_inventory) == 0
assert isinstance(ctx.selected_tests, defaultdict)
assert len(ctx.selected_tests) == 0
assert isinstance(ctx.devices_filtered_at_setup, list)
assert len(ctx.devices_filtered_at_setup) == 0
assert isinstance(ctx.devices_unreachable_at_setup, list)
assert len(ctx.devices_unreachable_at_setup) == 0
assert isinstance(ctx.warnings_at_setup, list)
assert len(ctx.warnings_at_setup) == 0
assert ctx.start_time is None
assert ctx.end_time is None
# Test properties
assert ctx.total_devices_in_inventory == 0
assert ctx.total_devices_filtered_by_tags == 0
assert ctx.total_devices_unreachable == 0
assert ctx.total_devices_selected_for_testing == 0
assert ctx.total_tests_scheduled == 0
assert ctx.duration is None

View file

@ -23,6 +23,7 @@ from anta.custom_types import (
REGEXP_TYPE_VXLAN_SRC_INTERFACE,
aaa_group_prefix,
bgp_multiprotocol_capabilities_abbreviations,
convert_reload_cause,
interface_autocomplete,
interface_case_sensitivity,
snmp_v3_prefix,
@ -267,3 +268,28 @@ def test_snmp_v3_prefix_valid_input() -> None:
assert snmp_v3_prefix("auth") == "v3Auth"
assert snmp_v3_prefix("noauth") == "v3NoAuth"
assert snmp_v3_prefix("priv") == "v3Priv"
@pytest.mark.parametrize(
("str_input", "expected_output"),
[
pytest.param("Ztp", "System reloaded due to Zero Touch Provisioning", id="valid"),
pytest.param("USER", "Reload requested by the user.", id="valid"),
pytest.param("fpga", "Reload requested after FPGA upgrade", id="valid"),
],
)
def test_convert_reload_cause(str_input: str, expected_output: str) -> None:
"""Test convert_reload_cause."""
assert convert_reload_cause(str_input) == expected_output
@pytest.mark.parametrize(
("str_input"),
[
pytest.param("ztp2", id="invalid"),
],
)
def test_invalid_convert_reload_cause(str_input: str) -> None:
"""Test invalid convert_reload_cause."""
with pytest.raises(ValueError, match=r"Invalid reload cause: 'ztp2' - expected causes are \['ZTP', 'USER', 'FPGA'\]"):
convert_reload_cause(str_input)

View file

@ -429,29 +429,8 @@ REFRESH_PARAMS: list[ParameterSet] = [
pytest.param(
{},
(
{"return_value": False},
{
"return_value": {
"mfgName": "Arista",
"modelName": "DCS-7280CR3-32P4-F",
"hardwareRevision": "11.00",
"serialNumber": "JPE19500066",
"systemMacAddress": "fc:bd:67:3d:13:c5",
"hwMacAddress": "fc:bd:67:3d:13:c5",
"configMacAddress": "00:00:00:00:00:00",
"version": "4.31.1F-34361447.fraserrel (engineering build)",
"architecture": "x86_64",
"internalVersion": "4.31.1F-34361447.fraserrel",
"internalBuildId": "4940d112-a2fc-4970-8b5a-a16cd03fd08c",
"imageFormatVersion": "3.0",
"imageOptimization": "Default",
"bootupTimestamp": 1700729434.5892005,
"uptime": 20666.78,
"memTotal": 8099732,
"memFree": 4989568,
"isIntlVersion": False,
}
},
{"side_effect": HTTPError(message="Unauthorized")},
{},
),
{"is_online": False, "established": False, "hw_model": None},
id="is not online",
@ -613,6 +592,10 @@ class TestAntaDevice:
"""
assert device.cache_statistics == expected
def test_max_connections(self, device: AntaDevice) -> None:
"""Test max_connections property."""
assert device.max_connections is None
class TestAsyncEOSDevice:
"""Test for anta.device.AsyncEOSDevice."""
@ -646,6 +629,16 @@ class TestAsyncEOSDevice:
else:
assert dev1 != dev2
def test_max_connections(self, async_device: AsyncEOSDevice) -> None:
"""Test max_connections property."""
# HTTPX uses a max_connections of 100 by default
assert async_device.max_connections == 100
def test_max_connections_none(self, async_device: AsyncEOSDevice) -> None:
"""Test max_connections property when not available in the session object."""
with patch.object(async_device, "_session", None):
assert async_device.max_connections is None
@pytest.mark.parametrize(
("async_device", "patch_kwargs", "expected"),
REFRESH_PARAMS,
@ -653,9 +646,9 @@ class TestAsyncEOSDevice:
)
async def test_refresh(self, async_device: AsyncEOSDevice, patch_kwargs: list[dict[str, Any]], expected: dict[str, Any]) -> None:
"""Test AsyncEOSDevice.refresh()."""
with patch.object(async_device._session, "check_connection", **patch_kwargs[0]), patch.object(async_device._session, "cli", **patch_kwargs[1]):
with patch.object(async_device._session, "check_api_endpoint", **patch_kwargs[0]), patch.object(async_device._session, "cli", **patch_kwargs[1]):
await async_device.refresh()
async_device._session.check_connection.assert_called_once() # type: ignore[attr-defined] # asynceapi.Device.check_connection is patched
async_device._session.check_api_endpoint.assert_called_once() # type: ignore[attr-defined] # asynceapi.Device.check_api_endpoint is patched
if expected["is_online"]:
async_device._session.cli.assert_called_once() # type: ignore[attr-defined] # asynceapi.Device.cli is patched
assert async_device.is_online == expected["is_online"]

View file

@ -298,82 +298,30 @@ class FakeTestWithMissingTest(AntaTest):
commands: ClassVar[list[AntaCommand | AntaTemplate]] = []
ANTATEST_DATA: list[dict[str, Any]] = [
{
"name": "no input",
"test": FakeTest,
"inputs": None,
"expected": {"__init__": {"result": "unset"}, "test": {"result": "success"}},
},
{
"name": "extra input",
"test": FakeTest,
ANTATEST_DATA: dict[tuple[type[AntaTest], str], Any] = {
(FakeTest, "no input"): {"inputs": None, "expected": {"__init__": {"result": "unset"}, "test": {"result": "success"}}},
(FakeTest, "extra input"): {
"inputs": {"string": "culpa! veniam quas quas veniam molestias, esse"},
"expected": {
"__init__": {
"result": "error",
"messages": ["Extra inputs are not permitted"],
},
"test": {"result": "error"},
},
"expected": {"__init__": {"result": "error", "messages": ["Extra inputs are not permitted"]}, "test": {"result": "error"}},
},
{
"name": "no input",
"test": FakeTestWithInput,
"inputs": None,
"expected": {
"__init__": {"result": "error", "messages": ["Field required"]},
"test": {"result": "error"},
},
},
{
"name": "wrong input type",
"test": FakeTestWithInput,
(FakeTestWithInput, "no input"): {"inputs": None, "expected": {"__init__": {"result": "error", "messages": ["Field required"]}, "test": {"result": "error"}}},
(FakeTestWithInput, "wrong input type"): {
"inputs": {"string": 1},
"expected": {
"__init__": {
"result": "error",
"messages": ["Input should be a valid string"],
},
"test": {"result": "error"},
},
"expected": {"__init__": {"result": "error", "messages": ["Input should be a valid string"]}, "test": {"result": "error"}},
},
{
"name": "good input",
"test": FakeTestWithInput,
(FakeTestWithInput, "good input"): {
"inputs": {"string": "culpa! veniam quas quas veniam molestias, esse"},
"expected": {
"__init__": {"result": "unset"},
"test": {
"result": "success",
"messages": ["culpa! veniam quas quas veniam molestias, esse"],
},
},
"expected": {"__init__": {"result": "unset"}, "test": {"result": "success", "messages": ["culpa! veniam quas quas veniam molestias, esse"]}},
},
{
"name": "good input",
"test": FakeTestWithTemplate,
(FakeTestWithTemplate, "good input"): {
"inputs": {"interface": "Ethernet1"},
"expected": {
"__init__": {"result": "unset"},
"test": {"result": "success", "messages": ["show interface Ethernet1"]},
},
"expected": {"__init__": {"result": "unset"}, "test": {"result": "success", "messages": ["show interface Ethernet1"]}},
},
{
"name": "wrong input type",
"test": FakeTestWithTemplate,
(FakeTestWithTemplate, "wrong input type"): {
"inputs": {"interface": 1},
"expected": {
"__init__": {
"result": "error",
"messages": ["Input should be a valid string"],
},
"test": {"result": "error"},
},
"expected": {"__init__": {"result": "error", "messages": ["Input should be a valid string"]}, "test": {"result": "error"}},
},
{
"name": "wrong render definition",
"test": FakeTestWithTemplateNoRender,
(FakeTestWithTemplateNoRender, "wrong render definition"): {
"inputs": {"interface": "Ethernet1"},
"expected": {
"__init__": {
@ -383,9 +331,7 @@ ANTATEST_DATA: list[dict[str, Any]] = [
"test": {"result": "error"},
},
},
{
"name": "AntaTemplateRenderError",
"test": FakeTestWithTemplateBadRender1,
(FakeTestWithTemplateBadRender1, "AntaTemplateRenderError"): {
"inputs": {"interface": "Ethernet1"},
"expected": {
"__init__": {
@ -395,125 +341,57 @@ ANTATEST_DATA: list[dict[str, Any]] = [
"test": {"result": "error"},
},
},
{
"name": "RuntimeError in render()",
"test": FakeTestWithTemplateBadRender2,
(FakeTestWithTemplateBadRender2, "RuntimeError in render()"): {
"inputs": {"interface": "Ethernet1"},
"expected": {
"__init__": {
"result": "error",
"messages": ["Exception in tests.units.test_models.FakeTestWithTemplateBadRender2.render(): RuntimeError"],
},
"__init__": {"result": "error", "messages": ["Exception in tests.units.test_models.FakeTestWithTemplateBadRender2.render(): RuntimeError"]},
"test": {"result": "error"},
},
},
{
"name": "Extra template parameters in render()",
"test": FakeTestWithTemplateBadRender3,
(FakeTestWithTemplateBadRender3, "Extra template parameters in render()"): {
"inputs": {"interface": "Ethernet1"},
"expected": {
"__init__": {
"result": "error",
"messages": [
"Exception in tests.units.test_models.FakeTestWithTemplateBadRender3.render(): ValidationError: 1 validation error for AntaParams\n"
"extra\n"
"Exception in tests.units.test_models.FakeTestWithTemplateBadRender3.render(): ValidationError: 1 validation error for AntaParams\nextra\n"
" Extra inputs are not permitted [type=extra_forbidden, input_value='blah', input_type=str]\n"
],
},
"test": {"result": "error"},
},
},
{
"name": "Access undefined template param in test()",
"test": FakeTestWithTemplateBadTest,
(FakeTestWithTemplateBadTest, "Access undefined template param in test()"): {
"inputs": {"interface": "Ethernet1"},
"expected": {
"__init__": {"result": "unset"},
"test": {"result": "error", "messages": ["AttributeError: 'AntaParams' object has no attribute 'wrong_template_param'"]},
},
},
{
"name": "unskip on platforms",
"test": UnSkipOnPlatformTest,
(UnSkipOnPlatformTest, "unskip on platforms"): {"inputs": None, "expected": {"__init__": {"result": "unset"}, "test": {"result": "success"}}},
(SkipOnPlatformTest, "skip on platforms, unset"): {"inputs": None, "expected": {"__init__": {"result": "unset"}, "test": {"result": "skipped"}}},
(SkipOnPlatformTestWithInput, "skip on platforms, not unset"): {
"inputs": None,
"expected": {
"__init__": {"result": "unset"},
"test": {"result": "success"},
},
"expected": {"__init__": {"result": "error", "messages": ["Field required"]}, "test": {"result": "error"}},
},
{
"name": "skip on platforms, unset",
"test": SkipOnPlatformTest,
(DeprecatedTestWithoutNewTest, "deprecate test without new test"): {
"inputs": None,
"expected": {
"__init__": {"result": "unset"},
"test": {"result": "skipped"},
},
"expected": {"__init__": {"result": "unset"}, "test": {"result": "success"}},
},
{
"name": "skip on platforms, not unset",
"test": SkipOnPlatformTestWithInput,
(DeprecatedTestWithNewTest, "deprecate test with new test"): {"inputs": None, "expected": {"__init__": {"result": "unset"}, "test": {"result": "success"}}},
(FakeTestWithFailedCommand, "failed command"): {
"inputs": None,
"expected": {
"__init__": {"result": "error", "messages": ["Field required"]},
"test": {"result": "error"},
},
"expected": {"__init__": {"result": "unset"}, "test": {"result": "error", "messages": ["show version has failed: failed command"]}},
},
{
"name": "deprecate test without new test",
"test": DeprecatedTestWithoutNewTest,
(FakeTestWithUnsupportedCommand, "unsupported command"): {
"inputs": None,
"expected": {
"__init__": {"result": "unset"},
"test": {"result": "success"},
},
"expected": {"__init__": {"result": "unset"}, "test": {"result": "skipped", "messages": ["'show hardware counter drop' is not supported on pytest"]}},
},
{
"name": "deprecate test with new test",
"test": DeprecatedTestWithNewTest,
(FakeTestWithKnownEOSError, "known EOS error command"): {
"inputs": None,
"expected": {
"__init__": {"result": "unset"},
"test": {"result": "success"},
},
"expected": {"__init__": {"result": "unset"}, "test": {"result": "failure", "messages": ["BGP inactive"]}},
},
{
"name": "failed command",
"test": FakeTestWithFailedCommand,
"inputs": None,
"expected": {
"__init__": {"result": "unset"},
"test": {
"result": "error",
"messages": ["show version has failed: failed command"],
},
},
},
{
"name": "unsupported command",
"test": FakeTestWithUnsupportedCommand,
"inputs": None,
"expected": {
"__init__": {"result": "unset"},
"test": {
"result": "skipped",
"messages": ["'show hardware counter drop' is not supported on pytest"],
},
},
},
{
"name": "known EOS error command",
"test": FakeTestWithKnownEOSError,
"inputs": None,
"expected": {
"__init__": {"result": "unset"},
"test": {
"result": "failure",
"messages": ["BGP inactive"],
},
},
},
]
}
BLACKLIST_COMMANDS_PARAMS = ["reload", "reload now", "reload --force", "write", "wr mem", "write memory", "conf t", "configure terminal", "configure session"]
@ -597,18 +475,20 @@ class TestAntaTest:
for result_msg, expected_msg in zip(test.result.messages, expected["messages"]): # NOTE: zip(strict=True) has been added in Python 3.10
assert expected_msg in result_msg
@pytest.mark.parametrize("data", ANTATEST_DATA, ids=build_test_id)
def test__init__(self, device: AntaDevice, data: dict[str, Any]) -> None:
@pytest.mark.parametrize("data", ANTATEST_DATA.items(), ids=build_test_id)
def test__init__(self, device: AntaDevice, data: tuple[tuple[type[AntaTest], str], Any]) -> None:
"""Test the AntaTest constructor."""
expected = data["expected"]["__init__"]
test = data["test"](device, inputs=data["inputs"])
(anta_test, test_data) = data
expected = test_data["expected"]["__init__"]
test = anta_test[0](device, inputs=test_data["inputs"])
self._assert_test(test, expected)
@pytest.mark.parametrize("data", ANTATEST_DATA, ids=build_test_id)
def test_test(self, device: AntaDevice, data: dict[str, Any]) -> None:
@pytest.mark.parametrize("data", ANTATEST_DATA.items(), ids=build_test_id)
def test_test(self, device: AntaDevice, data: tuple[tuple[type[AntaTest], str], Any]) -> None:
"""Test the AntaTest.test method."""
expected = data["expected"]["test"]
test = data["test"](device, inputs=data["inputs"])
(anta_test, test_data) = data
expected = test_data["expected"]["test"]
test = anta_test[0](device, inputs=test_data["inputs"])
asyncio.run(test.test())
self._assert_test(test, expected)

View file

@ -7,18 +7,19 @@ from __future__ import annotations
import logging
import os
import sys
from pathlib import Path
from typing import TYPE_CHECKING
from unittest.mock import patch
import pytest
from anta.catalog import AntaCatalog
from anta.inventory import AntaInventory
from anta.result_manager import ResultManager
from anta.runner import main, prepare_tests
from anta.runner import prepare_tests
from .test_models import FakeTest, FakeTestWithMissingTest
from .test_models import FakeTest
if TYPE_CHECKING:
from anta.inventory import AntaInventory
if os.name == "posix":
# The function is not defined on non-POSIX system
@ -30,47 +31,8 @@ DATA_DIR: Path = Path(__file__).parent.parent.resolve() / "data"
FAKE_CATALOG: AntaCatalog = AntaCatalog.from_list([(FakeTest, None)])
async def test_empty_tests(caplog: pytest.LogCaptureFixture, inventory: AntaInventory) -> None:
"""Test that when the list of tests is empty, a log is raised."""
caplog.set_level(logging.INFO)
manager = ResultManager()
await main(manager, inventory, AntaCatalog())
assert len(caplog.record_tuples) == 1
assert "The list of tests is empty, exiting" in caplog.records[0].message
async def test_empty_inventory(caplog: pytest.LogCaptureFixture) -> None:
"""Test that when the Inventory is empty, a log is raised."""
caplog.set_level(logging.INFO)
manager = ResultManager()
await main(manager, AntaInventory(), FAKE_CATALOG)
assert len(caplog.record_tuples) == 3
assert "The inventory is empty, exiting" in caplog.records[1].message
@pytest.mark.parametrize(
("inventory", "tags", "devices"),
[
pytest.param({"count": 1, "reachable": False}, None, None, id="not-reachable"),
pytest.param({"filename": "test_inventory_with_tags.yml", "reachable": False}, {"leaf"}, None, id="not-reachable-with-tag"),
pytest.param({"count": 1, "reachable": True}, {"invalid-tag"}, None, id="reachable-with-invalid-tag"),
pytest.param({"filename": "test_inventory_with_tags.yml", "reachable": True}, None, {"invalid-device"}, id="reachable-with-invalid-device"),
pytest.param({"filename": "test_inventory_with_tags.yml", "reachable": False}, None, {"leaf1"}, id="not-reachable-with-device"),
pytest.param({"filename": "test_inventory_with_tags.yml", "reachable": False}, {"leaf"}, {"leaf1"}, id="not-reachable-with-device-and-tag"),
pytest.param({"filename": "test_inventory_with_tags.yml", "reachable": False}, {"invalid"}, {"invalid-device"}, id="reachable-with-invalid-tag-and-device"),
],
indirect=["inventory"],
)
async def test_no_selected_device(caplog: pytest.LogCaptureFixture, inventory: AntaInventory, tags: set[str], devices: set[str]) -> None:
"""Test that when the list of established devices is empty a log is raised."""
caplog.set_level(logging.WARNING)
manager = ResultManager()
await main(manager, inventory, FAKE_CATALOG, tags=tags, devices=devices)
msg = f"No reachable device {f'matching the tags {tags} ' if tags else ''}was found.{f' Selected devices: {devices} ' if devices is not None else ''}"
assert msg in caplog.messages
# TODO: Remove this in ANTA v2.0.0
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.skipif(os.name != "posix", reason="Cannot run this test on Windows")
def test_adjust_rlimit_nofile_valid_env(caplog: pytest.LogCaptureFixture) -> None:
"""Test adjust_rlimit_nofile with valid environment variables."""
@ -104,9 +66,11 @@ def test_adjust_rlimit_nofile_valid_env(caplog: pytest.LogCaptureFixture) -> Non
setrlimit_mock.assert_called_once_with(resource.RLIMIT_NOFILE, (20480, 1048576))
# TODO: Remove this in ANTA v2.0.0
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.skipif(os.name != "posix", reason="Cannot run this test on Windows")
def test_adjust_rlimit_nofile_invalid_env(caplog: pytest.LogCaptureFixture) -> None:
"""Test adjust_rlimit_nofile with valid environment variables."""
"""Test adjust_rlimit_nofile with invalid environment variables."""
with (
caplog.at_level(logging.DEBUG),
patch.dict("os.environ", {"ANTA_NOFILE": "invalid"}),
@ -138,31 +102,42 @@ def test_adjust_rlimit_nofile_invalid_env(caplog: pytest.LogCaptureFixture) -> N
setrlimit_mock.assert_called_once_with(resource.RLIMIT_NOFILE, (16384, 1048576))
@pytest.mark.skipif(os.name == "posix", reason="Run this test on Windows only")
async def test_check_runner_log_for_windows(caplog: pytest.LogCaptureFixture, inventory: AntaInventory) -> None:
"""Test log output for Windows host regarding rlimit."""
caplog.set_level(logging.INFO)
manager = ResultManager()
# Using dry-run to shorten the test
await main(manager, inventory, FAKE_CATALOG, dry_run=True)
assert "Running on a non-POSIX system, cannot adjust the maximum number of file descriptors." in caplog.records[-3].message
# We could instead merge multiple coverage report together but that requires more work than just this.
@pytest.mark.skipif(os.name != "posix", reason="Fake non-posix for coverage")
async def test_check_runner_log_for_windows_fake(caplog: pytest.LogCaptureFixture, inventory: AntaInventory) -> None:
"""Test log output for Windows host regarding rlimit."""
with patch("os.name", new="win32"):
del sys.modules["anta.runner"]
from anta.runner import main # pylint: disable=W0621
caplog.set_level(logging.INFO)
manager = ResultManager()
# Using dry-run to shorten the test
await main(manager, inventory, FAKE_CATALOG, dry_run=True)
assert "Running on a non-POSIX system, cannot adjust the maximum number of file descriptors." in caplog.records[-3].message
# TODO: Remove this in ANTA v2.0.0
@pytest.mark.skipif(os.name != "posix", reason="Cannot run this test on Windows")
def test_adjust_rlimit_nofile_value_error(caplog: pytest.LogCaptureFixture) -> None:
"""Test adjust_rlimit_nofile with invalid environment variables."""
with (
caplog.at_level(logging.DEBUG),
patch.dict("os.environ", {"ANTA_NOFILE": "666"}),
patch("anta.runner.resource.getrlimit") as getrlimit_mock,
patch("anta.runner.resource.setrlimit") as setrlimit_mock,
):
# Simulate the default system limits
system_limits = (8192, 1048576)
# Setup getrlimit mock return value
getrlimit_mock.return_value = system_limits
# Simulate setrlimit behavior raising ValueError
def side_effect_setrlimit(_resource_id: int, _limits: tuple[int, int]) -> None:
msg = "not allowed to raise maximum limit"
raise ValueError(msg)
setrlimit_mock.side_effect = side_effect_setrlimit
result = adjust_rlimit_nofile()
# Assert the limits were *NOT* updated as expected
assert result == system_limits
assert "Initial limit numbers for open file descriptors for the current ANTA process: Soft Limit: 8192 | Hard Limit: 1048576" in caplog.text
assert caplog.records[-1].levelname == "WARNING"
assert "Failed to set soft limit for open file descriptors for the current ANTA process" in caplog.records[-1].getMessage()
setrlimit_mock.assert_called_once_with(resource.RLIMIT_NOFILE, (666, 1048576))
# TODO: Remove this in ANTA v2.0.0
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.parametrize(
("inventory", "tags", "tests", "devices_count", "tests_count"),
[
@ -190,29 +165,3 @@ async def test_prepare_tests(
assert selected_tests is not None
assert len(selected_tests) == devices_count
assert sum(len(tests) for tests in selected_tests.values()) == tests_count
async def test_dry_run(caplog: pytest.LogCaptureFixture, inventory: AntaInventory) -> None:
"""Test that when dry_run is True, no tests are run."""
caplog.set_level(logging.INFO)
manager = ResultManager()
await main(manager, inventory, FAKE_CATALOG, dry_run=True)
assert "Dry-run mode, exiting before running the tests." in caplog.records[-1].message
async def test_cannot_create_test(caplog: pytest.LogCaptureFixture, inventory: AntaInventory) -> None:
"""Test that when an Exception is raised during test instantiation, it is caught and a log is raised."""
caplog.set_level(logging.CRITICAL)
manager = ResultManager()
catalog = AntaCatalog.from_list([(FakeTestWithMissingTest, None)]) # type: ignore[type-abstract]
await main(manager, inventory, catalog)
msg = (
"There is an error when creating test tests.units.test_models.FakeTestWithMissingTest.\nIf this is not a custom test implementation: "
"Please reach out to the maintainer team or open an issue on Github: https://github.com/aristanetworks/anta.\nTypeError: "
)
msg += (
"Can't instantiate abstract class FakeTestWithMissingTest without an implementation for abstract method 'test'"
if sys.version_info >= (3, 12)
else "Can't instantiate abstract class FakeTestWithMissingTest with abstract method test"
)
assert msg in caplog.messages

View file

@ -0,0 +1,126 @@
# 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.
"""Unit tests for the anta.settings module."""
from __future__ import annotations
import logging
import os
import sys
from unittest.mock import patch
import pytest
from pydantic import ValidationError
from anta.settings import DEFAULT_MAX_CONCURRENCY, DEFAULT_NOFILE, AntaRunnerSettings
if os.name == "posix":
# The function is not defined on non-POSIX system
import resource
class TestAntaRunnerSettings:
"""Tests for the AntaRunnerSettings class."""
def test_defaults(self, setenvvar: pytest.MonkeyPatch) -> None:
"""Test defaults for ANTA runner settings."""
settings = AntaRunnerSettings()
assert settings.nofile == DEFAULT_NOFILE
assert settings.max_concurrency == DEFAULT_MAX_CONCURRENCY
def test_env_var(self, setenvvar: pytest.MonkeyPatch) -> None:
"""Test setting different ANTA runner settings."""
setenvvar.setenv("ANTA_NOFILE", "20480")
settings = AntaRunnerSettings()
assert settings.nofile == 20480
assert settings.max_concurrency == DEFAULT_MAX_CONCURRENCY
def test_validation(self, setenvvar: pytest.MonkeyPatch) -> None:
"""Test validation of ANTA runner settings."""
setenvvar.setenv("ANTA_NOFILE", "-1")
with pytest.raises(ValidationError):
AntaRunnerSettings()
setenvvar.setenv("ANTA_MAX_CONCURRENCY", "0")
with pytest.raises(ValidationError):
AntaRunnerSettings()
@pytest.mark.skipif(os.name == "posix", reason="Run this test on Windows only")
def test_file_descriptor_limit_windows(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test file_descriptor_limit on Windows."""
caplog.set_level(logging.INFO)
settings = AntaRunnerSettings()
assert settings.file_descriptor_limit == sys.maxsize
assert "Running on a non-POSIX system, cannot adjust the maximum number of file descriptors." in caplog.text
@pytest.mark.skipif(os.name != "posix", reason="Fake non-posix for coverage")
async def test_file_descriptor_limit_fake_windows(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test file_descriptor_limit on fake Windows."""
caplog.set_level(logging.INFO)
with patch("os.name", new="win32"):
settings = AntaRunnerSettings()
assert settings.file_descriptor_limit == sys.maxsize
assert "Running on a non-POSIX system, cannot adjust the maximum number of file descriptors." in caplog.records[0].message
@pytest.mark.skipif(os.name != "posix", reason="Cannot run this test on Windows")
def test_file_descriptor_limit_posix(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test file_descriptor_limit on POSIX systems."""
with (
caplog.at_level(logging.DEBUG),
patch.dict("os.environ", {"ANTA_NOFILE": "20480"}),
patch("resource.getrlimit") as getrlimit_mock,
patch("resource.setrlimit") as setrlimit_mock,
):
# Simulate the default system limits
system_limits = (8192, 1048576)
# Setup getrlimit mock return value
getrlimit_mock.return_value = system_limits
# Simulate setrlimit behavior
def side_effect_setrlimit(_resource_id: int, limits: tuple[int, int]) -> None:
getrlimit_mock.return_value = (limits[0], limits[1])
setrlimit_mock.side_effect = side_effect_setrlimit
settings = AntaRunnerSettings()
# Assert the limits were updated as expected
assert settings.file_descriptor_limit == 20480
assert "Initial file descriptor limits for the current ANTA process: Soft Limit: 8192 | Hard Limit: 1048576" in caplog.text
assert "Setting file descriptor soft limit to 20480" in caplog.text
setrlimit_mock.assert_called_once_with(resource.RLIMIT_NOFILE, (20480, 1048576)) # pylint: disable=possibly-used-before-assignment
@pytest.mark.skipif(os.name != "posix", reason="Cannot run this test on Windows")
def test_file_descriptor_limit_value_error(self, caplog: pytest.LogCaptureFixture) -> None:
"""Test file_descriptor_limit with invalid environment variables."""
with (
caplog.at_level(logging.DEBUG),
patch.dict("os.environ", {"ANTA_NOFILE": "666"}),
patch("resource.getrlimit") as getrlimit_mock,
patch("resource.setrlimit") as setrlimit_mock,
):
# Simulate the default system limits
system_limits = (32768, 131072)
# Setup getrlimit mock return value
getrlimit_mock.return_value = system_limits
# Simulate setrlimit behavior raising ValueError
def side_effect_setrlimit(_resource_id: int, _limits: tuple[int, int]) -> None:
msg = "not allowed to raise maximum limit"
raise ValueError(msg)
setrlimit_mock.side_effect = side_effect_setrlimit
settings = AntaRunnerSettings()
# Assert the limits were *NOT* updated as expected
assert settings.file_descriptor_limit == 32768
assert "Initial file descriptor limits for the current ANTA process: Soft Limit: 32768 | Hard Limit: 131072" in caplog.text
assert caplog.records[-1].levelname == "WARNING"
assert "Failed to set file descriptor soft limit for the current ANTA process" in caplog.records[-1].getMessage()
setrlimit_mock.assert_called_once_with(resource.RLIMIT_NOFILE, (666, 131072))