Adding 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:27 +02:00
parent dc7df702ea
commit 7996c81031
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
166 changed files with 13787 additions and 11959 deletions

View file

@ -37,6 +37,7 @@ def test_anta_dry_run(
results = session_results[request.node.callspec.id]
# TODO: Use AntaRunner directly in ANTA v2.0.0
@benchmark
def _() -> None:
results.reset()
@ -69,6 +70,7 @@ def test_anta(
results = session_results[request.node.callspec.id]
# TODO: Use AntaRunner directly in ANTA v2.0.0
@benchmark
def _() -> None:
results.reset()
@ -77,21 +79,6 @@ def test_anta(
logging.disable(logging.NOTSET)
if len(catalog.tests) * len(inventory) != len(results.results):
# This could mean duplicates exist.
# TODO: consider removing this code and refactor unit test data as a dictionary with tuple keys instead of a list
seen = set()
dupes = []
for test in catalog.tests:
if test in seen:
dupes.append(test)
else:
seen.add(test)
if dupes:
for test in dupes:
msg = f"Found duplicate in test catalog: {test}"
logger.error(msg)
pytest.fail(f"Expected {len(catalog.tests) * len(inventory)} tests but got {len(results.results)}", pytrace=False)
bench_info = (
"\n--- ANTA NRFU Benchmark Information ---\n"
f"Test results: {len(results.results)}\n"

View file

@ -67,5 +67,6 @@ def test_csv(results: ResultManager, tmp_path: Path) -> None:
@pytest.mark.benchmark
@pytest.mark.dependency(depends=["anta_benchmark"], scope="package")
def test_markdown(results: ResultManager, tmp_path: Path) -> None:
"""Benchmark MDReportGenerator.generate()."""
MDReportGenerator.generate(results=results, md_filename=tmp_path / "report.md")
"""Benchmark MDReportGenerator.generate_sections()."""
sections = [(section, results) for section in MDReportGenerator.DEFAULT_SECTIONS]
MDReportGenerator.generate_sections(sections=sections, md_filename=tmp_path / "report.md")

View file

@ -7,6 +7,9 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Any
import pytest
from anta._runner import AntaRunContext, AntaRunFilters, AntaRunner
from anta.result_manager import ResultManager
from anta.runner import get_coroutines, prepare_tests
@ -22,6 +25,8 @@ if TYPE_CHECKING:
from anta.result_manager.models import TestResult
# TODO: Remove this in ANTA v2.0.0
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_prepare_tests(benchmark: BenchmarkFixture, catalog: AntaCatalog, inventory: AntaInventory) -> None:
"""Benchmark `anta.runner.prepare_tests`."""
@ -36,6 +41,8 @@ def test_prepare_tests(benchmark: BenchmarkFixture, catalog: AntaCatalog, invent
assert sum(len(tests) for tests in selected_tests.values()) == len(inventory) * len(catalog.tests)
# TODO: Remove this in ANTA v2.0.0
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_get_coroutines(benchmark: BenchmarkFixture, catalog: AntaCatalog, inventory: AntaInventory) -> None:
"""Benchmark `anta.runner.get_coroutines`."""
selected_tests = prepare_tests(inventory=inventory, catalog=catalog, tests=None, tags=None)
@ -52,3 +59,38 @@ def test_get_coroutines(benchmark: BenchmarkFixture, catalog: AntaCatalog, inven
count = sum(len(tests) for tests in selected_tests.values())
assert count == len(coroutines)
def test__setup_tests(benchmark: BenchmarkFixture, catalog: AntaCatalog, inventory: AntaInventory) -> None:
"""Benchmark `anta._runner.AntaRunner._setup_tests`."""
runner = AntaRunner()
ctx = AntaRunContext(inventory=inventory, catalog=catalog, manager=ResultManager(), filters=AntaRunFilters(), selected_inventory=inventory)
def bench() -> None:
catalog.clear_indexes()
runner._setup_tests(ctx)
benchmark(bench)
assert ctx.total_tests_scheduled != 0
assert ctx.total_devices_selected_for_testing == len(inventory)
assert ctx.total_tests_scheduled == len(inventory) * len(catalog.tests)
def test__get_test_coroutines(benchmark: BenchmarkFixture, catalog: AntaCatalog, inventory: AntaInventory) -> None:
"""Benchmark `anta._runner.AntaRunner._get_test_coroutines`."""
runner = AntaRunner()
ctx = AntaRunContext(inventory=inventory, catalog=catalog, manager=ResultManager(), filters=AntaRunFilters(), selected_inventory=inventory)
runner._setup_tests(ctx)
assert ctx.selected_tests is not None
def bench() -> list[Coroutine[Any, Any, TestResult]]:
coros = runner._get_test_coroutines(ctx)
for c in coros:
c.close()
return coros
coroutines = benchmark(bench)
assert ctx.total_tests_scheduled == len(coroutines)

View file

@ -50,13 +50,21 @@ class AntaMockEnvironment: # pylint: disable=too-few-public-methods
Also provide the attribute 'eos_data_catalog` with the output of all the commands used in the test catalog.
Each module in `tests.units.anta_tests` has a `DATA` constant.
The `DATA` structure is a list of dictionaries used to parametrize the test. The list elements have the following keys:
- `name` (str): Test name as displayed by Pytest.
- `test` (AntaTest): An AntaTest subclass imported in the test module - e.g. VerifyUptime.
- `eos_data` (list[dict]): List of data mocking EOS returned data to be passed to the test.
- `inputs` (dict): Dictionary to instantiate the `test` inputs as defined in the class from `test`.
The keys of `eos_data_catalog` is the tuple (DATA['test'], DATA['name']). The values are `eos_data`.
The `DATA` structure is a dictionary where:
- Each key is a tuple of size 2 containing:
- An AntaTest subclass imported in the test module as first element - e.g. VerifyUptime.
- A string used as name displayed by pytest as second element.
- Each value is an instance of AntaUnitTest, which is a Python TypedDict.
And AntaUnitTest have the following keys:
- `eos_data` (list[dict]): List of data mocking EOS returned data to be passed to the test.
- `inputs` (dict): Dictionary to instantiate the `test` inputs as defined in the class from `test`.
- `expected` (dict): Expected test result structure, a dictionary containing a key `result` containing one of the allowed status
(`Literal[AntaTestStatus.SUCCESS, AntaTestStatus.FAILURE, AntaTestStatus.SKIPPED]`) and
optionally a key `messages` which is a list(str) and each message is expected to be a substring of one of the actual messages in the TestResult object.
The keys of `eos_data_catalog` is the tuple (AntaTest subclass, A string used as name displayed by pytest). The values are `eos_data`.
"""
def __init__(self) -> None:
@ -87,10 +95,11 @@ class AntaMockEnvironment: # pylint: disable=too-few-public-methods
test_definitions = []
eos_data_catalog = {}
for module in import_test_modules():
for test_data in module.DATA:
test = test_data["test"]
result_overwrite = AntaTest.Input.ResultOverwrite(custom_field=test_data["name"])
if test_data["inputs"] is None:
for (test, name), test_data in module.DATA.items():
# Extract the test class, name and test data from a nested tuple structure:
# unit test: Tuple[Tuple[Type[AntaTest], str], AntaUnitTest]
result_overwrite = AntaTest.Input.ResultOverwrite(custom_field=name)
if test_data.get("inputs") is None:
inputs = test.Input(result_overwrite=result_overwrite)
else:
inputs = test.Input(**test_data["inputs"], result_overwrite=result_overwrite)
@ -98,7 +107,7 @@ class AntaMockEnvironment: # pylint: disable=too-few-public-methods
test=test,
inputs=inputs,
)
eos_data_catalog[(test.__name__, test_data["name"])] = test_data["eos_data"]
eos_data_catalog[(test.__name__, name)] = test_data["eos_data"]
test_definitions.append(test_definition)
return (AntaCatalog(tests=test_definitions), eos_data_catalog)

View file

@ -3,10 +3,8 @@
# that can be found in the LICENSE file.
"""See https://docs.pytest.org/en/stable/reference/fixtures.html#conftest-py-sharing-fixtures-across-multiple-files."""
import asyncio
from collections.abc import Iterator
from pathlib import Path
from unittest.mock import AsyncMock, Mock, patch
import pytest
import respx
@ -42,7 +40,8 @@ def inventory(request: pytest.FixtureRequest) -> Iterator[AntaInventory]:
)
if reachable:
# This context manager makes all devices reachable
with patch("asyncio.open_connection", AsyncMock(spec=asyncio.open_connection, return_value=(Mock(), Mock()))), respx.mock:
with respx.mock:
respx.head(path="/command-api")
respx.post(path="/command-api", headers={"Content-Type": "application/json-rpc"}, json__params__cmds__0__cmd="show version").respond(
json={
"result": [
@ -54,5 +53,6 @@ def inventory(request: pytest.FixtureRequest) -> Iterator[AntaInventory]:
)
yield inv
else:
with patch("asyncio.open_connection", AsyncMock(spec=asyncio.open_connection, side_effect=TimeoutError)):
with respx.mock:
respx.head(path="/command-api").respond(status_code=401)
yield inv

View file

@ -1,7 +1,7 @@
# 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.
"""Syntax Error in file."""
# pylint: skip-file
# flake8: noqa
# type: ignore
typo
typo # type: ignore[name-defined] # noqa: B018

View file

@ -15,78 +15,233 @@
| Total Tests | Total Tests Success | Total Tests Skipped | Total Tests Failure | Total Tests Error |
| ----------- | ------------------- | ------------------- | ------------------- | ------------------|
| 30 | 4 | 9 | 15 | 2 |
| 181 | 43 | 34 | 103 | 1 |
### Summary Totals Device Under Test
| Device Under Test | Total Tests | Tests Success | Tests Skipped | Tests Failure | Tests Error | Categories Skipped | Categories Failed |
| ------------------| ----------- | ------------- | ------------- | ------------- | ----------- | -------------------| ------------------|
| s1-spine1 | 30 | 4 | 9 | 15 | 2 | AVT, Field Notices, Hardware, ISIS, LANZ, OSPF, PTP, Path-Selection, Profiles | AAA, BFD, BGP, Connectivity, Cvx, Interfaces, Logging, MLAG, SNMP, STUN, Security, Services, Software, System, VLAN |
| s1-spine1 | 181 | 43 | 34 | 103 | 1 | AVT, Field Notices, Flow Tracking, Hardware, ISIS, Interfaces, LANZ, OSPF, PTP, Path-Selection, Profiles, Segment-Routing | AAA, BFD, BGP, Configuration, Connectivity, Cvx, Greent, Interfaces, Logging, MLAG, Multicast, Routing, SNMP, STP, STUN, Security, Services, Software, VLAN, VXLAN |
### Summary Totals Per Category
| Test Category | Total Tests | Tests Success | Tests Skipped | Tests Failure | Tests Error |
| ------------- | ----------- | ------------- | ------------- | ------------- | ----------- |
| AAA | 1 | 0 | 0 | 1 | 0 |
| AVT | 1 | 0 | 1 | 0 | 0 |
| BFD | 1 | 0 | 0 | 1 | 0 |
| BGP | 1 | 0 | 0 | 0 | 1 |
| Configuration | 1 | 1 | 0 | 0 | 0 |
| Connectivity | 1 | 0 | 0 | 1 | 0 |
| Cvx | 1 | 0 | 0 | 0 | 1 |
| Field Notices | 1 | 0 | 1 | 0 | 0 |
| Hardware | 1 | 0 | 1 | 0 | 0 |
| Interfaces | 1 | 0 | 0 | 1 | 0 |
| ISIS | 1 | 0 | 1 | 0 | 0 |
| AAA | 7 | 0 | 0 | 7 | 0 |
| AVT | 3 | 0 | 3 | 0 | 0 |
| BFD | 4 | 1 | 0 | 3 | 0 |
| BGP | 25 | 3 | 0 | 21 | 1 |
| Configuration | 3 | 1 | 0 | 2 | 0 |
| Connectivity | 2 | 1 | 0 | 1 | 0 |
| Cvx | 5 | 0 | 0 | 5 | 0 |
| Field Notices | 2 | 0 | 2 | 0 | 0 |
| Flow Tracking | 1 | 0 | 1 | 0 | 0 |
| Greent | 2 | 0 | 0 | 2 | 0 |
| Hardware | 7 | 0 | 7 | 0 | 0 |
| Interfaces | 16 | 7 | 1 | 8 | 0 |
| ISIS | 7 | 0 | 7 | 0 | 0 |
| LANZ | 1 | 0 | 1 | 0 | 0 |
| Logging | 1 | 0 | 0 | 1 | 0 |
| MLAG | 1 | 0 | 0 | 1 | 0 |
| OSPF | 1 | 0 | 1 | 0 | 0 |
| Path-Selection | 1 | 0 | 1 | 0 | 0 |
| Profiles | 1 | 0 | 1 | 0 | 0 |
| PTP | 1 | 0 | 1 | 0 | 0 |
| Routing | 1 | 1 | 0 | 0 | 0 |
| Security | 2 | 0 | 0 | 2 | 0 |
| Services | 1 | 0 | 0 | 1 | 0 |
| SNMP | 1 | 0 | 0 | 1 | 0 |
| Software | 1 | 0 | 0 | 1 | 0 |
| STP | 1 | 1 | 0 | 0 | 0 |
| STUN | 2 | 0 | 0 | 2 | 0 |
| System | 1 | 0 | 0 | 1 | 0 |
| VLAN | 1 | 0 | 0 | 1 | 0 |
| VXLAN | 1 | 1 | 0 | 0 | 0 |
| Logging | 10 | 3 | 0 | 7 | 0 |
| MLAG | 6 | 4 | 0 | 2 | 0 |
| Multicast | 2 | 1 | 0 | 1 | 0 |
| OSPF | 3 | 0 | 3 | 0 | 0 |
| Path-Selection | 2 | 0 | 2 | 0 | 0 |
| Profiles | 2 | 0 | 2 | 0 | 0 |
| PTP | 5 | 0 | 5 | 0 | 0 |
| Routing | 6 | 2 | 0 | 4 | 0 |
| Security | 15 | 3 | 0 | 12 | 0 |
| Segment-Routing | 3 | 0 | 3 | 0 | 0 |
| Services | 4 | 1 | 0 | 3 | 0 |
| SNMP | 12 | 0 | 0 | 12 | 0 |
| Software | 3 | 1 | 0 | 2 | 0 |
| STP | 7 | 4 | 0 | 3 | 0 |
| STUN | 3 | 0 | 0 | 3 | 0 |
| System | 8 | 8 | 0 | 0 | 0 |
| VLAN | 3 | 0 | 0 | 3 | 0 |
| VXLAN | 5 | 3 | 0 | 2 | 0 |
## Test Results
| Device Under Test | Categories | Test | Description | Custom Field | Result | Messages |
| ----------------- | ---------- | ---- | ----------- | ------------ | ------ | -------- |
| s1-spine1 | AAA | VerifyAcctConsoleMethods | Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x). | - | failure | AAA console accounting is not configured for commands, exec, system, dot1x |
| s1-spine1 | AVT | VerifyAVTPathHealth | Verifies the status of all AVT paths for all VRFs. | - | skipped | VerifyAVTPathHealth test is not supported on cEOSLab. |
| s1-spine1 | BFD | VerifyBFDPeersHealth | Verifies the health of IPv4 BFD peers across all VRFs. | - | failure | No IPv4 BFD peers are configured for any VRF. |
| s1-spine1 | BGP | VerifyBGPAdvCommunities | Verifies that advertised communities are standard, extended and large for BGP IPv4 peer(s). | - | error | show bgp neighbors vrf all has failed: The command is only supported in the multi-agent routing protocol model., The command is only supported in the multi-agent routing protocol model., The command is only supported in the multi-agent routing protocol model., The command is only supported in the multi-agent routing protocol model. |
| s1-spine1 | Configuration | VerifyRunningConfigDiffs | Verifies there is no difference between the running-config and the startup-config. | - | success | - |
| s1-spine1 | AAA | VerifyAcctDefaultMethods | Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x). | - | failure | AAA default accounting is not configured for commands, exec, system, dot1x |
| s1-spine1 | AAA | VerifyAuthenMethods | Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x). | - | failure | AAA authentication methods are not configured for login console |
| s1-spine1 | AAA | VerifyAuthzMethods | Verifies the AAA authorization method lists for different authorization types (commands, exec). | - | failure | AAA authorization methods local, none, logging are not matching for commands, exec |
| s1-spine1 | AAA | VerifyTacacsServerGroups | Verifies if the provided TACACS server group(s) are configured. | - | failure | No TACACS server group(s) are configured |
| s1-spine1 | AAA | VerifyTacacsServers | Verifies TACACS servers are configured for a specified VRF. | - | failure | No TACACS servers are configured |
| s1-spine1 | AAA | VerifyTacacsSourceIntf | Verifies TACACS source-interface for a specified VRF. | - | failure | VRF: MGMT Source Interface: Management0 - Not configured |
| s1-spine1 | AVT | VerifyAVTPathHealth | Verifies the status of all AVT paths for all VRFs. | - | skipped | VerifyAVTPathHealth test is not supported on cEOSLab |
| s1-spine1 | AVT | VerifyAVTRole | Verifies the AVT role of a device. | - | skipped | VerifyAVTRole test is not supported on cEOSLab |
| s1-spine1 | AVT | VerifyAVTSpecificPath | Verifies the Adaptive Virtual Topology (AVT) path. | - | skipped | VerifyAVTSpecificPath test is not supported on cEOSLab |
| s1-spine1 | BFD | VerifyBFDPeersHealth | Verifies the health of IPv4 BFD peers across all VRFs. | - | success | - |
| s1-spine1 | BFD | VerifyBFDPeersIntervals | Verifies the timers of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.8 VRF: default - Not found<br>Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BFD | VerifyBFDPeersRegProtocols | Verifies the registered routing protocol of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BFD | VerifyBFDSpecificPeers | Verifies the state of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.8 VRF: default - Not found<br>Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPAdvCommunities | Verifies the advertised communities of BGP peers. | - | failure | Peer: 172.30.11.17 VRF: default - Not found<br>Peer: 172.30.11.21 VRF: MGMT - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPExchangedRoutes | Verifies the advertised and received routes of BGP IPv4 peer(s). | - | failure | Peer: 172.30.255.5 VRF: default Advertised route: 192.0.254.5/32 - Not found<br>Peer: 172.30.255.5 VRF: default Received route: 192.0.255.4/32 - Not found<br>Peer: 172.30.255.1 VRF: default Advertised route: 192.0.255.1/32 - Not found<br>Peer: 172.30.255.1 VRF: default Advertised route: 192.0.254.5/32 - Not found |
| s1-spine1 | BGP | VerifyBGPNlriAcceptance | Verifies that all received NLRI are accepted for all AFI/SAFI configured for BGP peers. | - | failure | Peer: 10.100.0.128 VRF: default - Not found<br>Peer: 2001:db8:1::2 VRF: default - Not found<br>Peer: fe80::2%Et1 VRF: default - Not found<br>Peer: fe80::2%Et1 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPPeerASNCap | Verifies the four octet ASN capability of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerCount | Verifies the count of BGP peers for given address families. | - | failure | AFI: ipv4 SAFI: unicast VRF: PROD - VRF not configured<br>AFI: ipv4 SAFI: multicast VRF: DEV - VRF not configured |
| s1-spine1 | BGP | VerifyBGPPeerDropStats | Verifies BGP NLRI drop statistics of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerGroup | Verifies BGP peer group of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerMD5Auth | Verifies the MD5 authentication and state of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.5 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: default - Session does not have MD5 authentication enabled |
| s1-spine1 | BGP | VerifyBGPPeerMPCaps | Verifies the multiprotocol capabilities of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: default - ipv4MplsLabels not found<br>Interface: Ethernet1 VRF: default - ipv4MplsVpn not found |
| s1-spine1 | BGP | VerifyBGPPeerRouteLimit | Verifies maximum routes and warning limit for BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerRouteRefreshCap | Verifies the route refresh capabilities of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerSession | Verifies the session state of BGP peers. | - | failure | Peer: 10.1.0.1 VRF: default - Not found<br>Peer: 10.1.0.2 VRF: default - Not found<br>Peer: 10.1.255.2 VRF: DEV - Not found<br>Peer: 10.1.255.4 VRF: DEV - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Vlan3499 VRF: PROD - Not found |
| s1-spine1 | BGP | VerifyBGPPeerSessionRibd | Verifies the session state of BGP peers. | - | failure | Peer: 10.1.0.1 VRF: default - Not found<br>Peer: 10.1.255.4 VRF: DEV - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerTtlMultiHops | Verifies BGP TTL and max-ttl-hops count for BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.2 VRF: test - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerUpdateErrors | Verifies BGP update error counters of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeersHealth | Verifies the health of BGP peers for given address families. | - | failure | AFI: ipv6 SAFI: unicast VRF: DEV - VRF not configured |
| s1-spine1 | BGP | VerifyBGPPeersHealthRibd | Verifies the health of all the BGP peers. | - | success | - |
| s1-spine1 | BGP | VerifyBGPRedistribution | Verifies BGP redistribution. | - | error | show bgp instance vrf all has failed: Invalid requested version for ModelMetaClass: no such revision 4, most current is 3 |
| s1-spine1 | BGP | VerifyBGPRouteECMP | Verifies BGP IPv4 route ECMP paths. | - | success | - |
| s1-spine1 | BGP | VerifyBGPRoutePaths | Verifies BGP IPv4 route paths. | - | success | - |
| s1-spine1 | BGP | VerifyBGPSpecificPeers | Verifies the health of specific BGP peer(s) for given address families. | - | failure | AFI: evpn Peer: 10.1.0.1 - Not configured<br>AFI: evpn Peer: 10.1.0.2 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.254.1 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.0 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.2 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.4 - Not configured |
| s1-spine1 | BGP | VerifyBGPTimers | Verifies the timers of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.5 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBgpRouteMaps | Verifies BGP inbound and outbound route-maps of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyEVPNType2Route | Verifies the EVPN Type-2 routes for a given IPv4 or MAC address and VNI. | - | failure | Address: 192.168.20.102 VNI: 10020 - No EVPN Type-2 route<br>Address: aa:c1:ab:5d:b4:1e VNI: 10010 - No EVPN Type-2 route |
| s1-spine1 | BGP | VerifyEVPNType5Routes | Verifies EVPN Type-5 routes for given IP prefixes and VNIs. | - | failure | Prefix: 192.168.10.0/24 VNI: 10 - No EVPN Type-5 routes found<br>Prefix: 192.168.20.0/24 VNI: 20 - No EVPN Type-5 routes found<br>Prefix: 192.168.30.0/24 VNI: 30 - No EVPN Type-5 routes found<br>Prefix: 192.168.40.0/24 VNI: 40 - No EVPN Type-5 routes found |
| s1-spine1 | Configuration | VerifyRunningConfigDiffs | Verifies there is no difference between the running-config and the startup-config. | - | failure | --- flash:/startup-config<br>+++ system:/running-config<br>@@ -18,6 +18,7 @@<br> hostname leaf1-dc1<br> ip name-server vrf MGMT 10.14.0.1<br> dns domain fun.aristanetworks.com<br>+ip host vitthal 192.168.66.220<br> !<br> platform tfa<br> personality arfa<br> |
| s1-spine1 | Configuration | VerifyRunningConfigLines | Search the Running-Config for the given RegEx patterns. | - | failure | Following patterns were not found: '^enable password.*$', 'bla bla' |
| s1-spine1 | Configuration | VerifyZeroTouch | Verifies ZeroTouch is disabled. | - | success | - |
| s1-spine1 | Connectivity | VerifyLLDPNeighbors | Verifies the connection status of the specified LLDP (Link Layer Discovery Protocol) neighbors. | - | failure | Port: Ethernet1 Neighbor: DC1-SPINE1 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: spine1-dc1.fun.aristanetworks.com/Ethernet3<br>Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: spine2-dc1.fun.aristanetworks.com/Ethernet3 |
| s1-spine1 | Cvx | VerifyActiveCVXConnections | Verifies the number of active CVX Connections. | - | error | show cvx connections brief has failed: Unavailable command (controller not ready) (at token 2: 'connections') |
| s1-spine1 | Field Notices | VerifyFieldNotice44Resolution | Verifies that the device is using the correct Aboot version per FN0044. | - | skipped | VerifyFieldNotice44Resolution test is not supported on cEOSLab. |
| s1-spine1 | Hardware | VerifyTemperature | Verifies if the device temperature is within acceptable limits. | - | skipped | VerifyTemperature test is not supported on cEOSLab. |
| s1-spine1 | Connectivity | VerifyReachability | Test network reachability to one or many destination IP(s). | - | success | - |
| s1-spine1 | Cvx | VerifyActiveCVXConnections | Verifies the number of active CVX Connections. | - | failure | 'show cvx connections brief' failed on s1-spine1: Unavailable command (controller not ready) (at token 2: 'connections') |
| s1-spine1 | Cvx | VerifyCVXClusterStatus | Verifies the CVX Server Cluster status. | - | failure | CVX Server status is not enabled<br>CVX Server is not a cluster |
| s1-spine1 | Cvx | VerifyManagementCVX | Verifies the management CVX global status. | - | failure | Management CVX status is not valid: Expected: enabled Actual: disabled |
| s1-spine1 | Cvx | VerifyMcsClientMounts | Verify if all MCS client mounts are in mountStateMountComplete. | - | failure | MCS Client mount states are not present |
| s1-spine1 | Cvx | VerifyMcsServerMounts | Verify if all MCS server mounts are in a MountComplete state. | - | failure | 'show cvx mounts' failed on s1-spine1: Unavailable command (controller not ready) (at token 2: 'mounts') |
| s1-spine1 | Field Notices | VerifyFieldNotice44Resolution | Verifies that the device is using the correct Aboot version per FN0044. | - | skipped | VerifyFieldNotice44Resolution test is not supported on cEOSLab |
| s1-spine1 | Field Notices | VerifyFieldNotice72Resolution | Verifies if the device is exposed to FN0072, and if the issue has been mitigated. | - | skipped | VerifyFieldNotice72Resolution test is not supported on cEOSLab |
| s1-spine1 | Flow Tracking | VerifyHardwareFlowTrackerStatus | Verifies the hardware flow tracking state. | - | skipped | VerifyHardwareFlowTrackerStatus test is not supported on cEOSLab |
| s1-spine1 | Greent | VerifyGreenT | Verifies if a GreenT policy other than the default is created. | - | failure | No GreenT policy is created |
| s1-spine1 | Greent | VerifyGreenTCounters | Verifies if the GreenT counters are incremented. | - | failure | GreenT counters are not incremented |
| s1-spine1 | Hardware | VerifyAdverseDrops | Verifies there are no adverse drops on DCS-7280 and DCS-7500 family switches. | - | skipped | VerifyAdverseDrops test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyEnvironmentCooling | Verifies the status of power supply fans and all fan trays. | - | skipped | VerifyEnvironmentCooling test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyEnvironmentPower | Verifies the power supplies status. | - | skipped | VerifyEnvironmentPower test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyEnvironmentSystemCooling | Verifies the device's system cooling status. | - | skipped | VerifyEnvironmentSystemCooling test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyTemperature | Verifies if the device temperature is within acceptable limits. | - | skipped | VerifyTemperature test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyTransceiversManufacturers | Verifies if all the transceivers come from approved manufacturers. | - | skipped | VerifyTransceiversManufacturers test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyTransceiversTemperature | Verifies if all the transceivers are operating at an acceptable temperature. | - | skipped | VerifyTransceiversTemperature test is not supported on cEOSLab |
| s1-spine1 | Interfaces | VerifyIPProxyARP | Verifies if Proxy ARP is enabled. | - | failure | Interface: Ethernet1 - Proxy-ARP disabled<br>Interface: Ethernet2 - Proxy-ARP disabled |
| s1-spine1 | Interfaces | VerifyIllegalLACP | Verifies there are no illegal LACP packets in all port channels. | - | success | - |
| s1-spine1 | Interfaces | VerifyInterfaceErrDisabled | Verifies there are no interfaces in the errdisabled state. | - | success | - |
| s1-spine1 | Interfaces | VerifyInterfaceErrors | Verifies that the interfaces error counters are equal to zero. | - | success | - |
| s1-spine1 | Interfaces | VerifyInterfaceIPv4 | Verifies the interface IPv4 addresses. | - | failure | Interface: Ethernet2 - IP address mismatch - Expected: 172.30.11.1/31 Actual: 10.100.0.11/31<br>Interface: Ethernet2 - Secondary IP address is not configured |
| s1-spine1 | Interfaces | VerifyInterfaceUtilization | Verifies that the utilization of interfaces is below a certain threshold. | - | success | - |
| s1-spine1 | Interfaces | VerifyInterfacesSpeed | Verifies the speed, lanes, auto-negotiation status, and mode as full duplex for interfaces. | - | failure | Interface: Ethernet2 - Bandwidth mismatch - Expected: 10.0Gbps Actual: 1Gbps<br>Interface: Ethernet3 - Bandwidth mismatch - Expected: 100.0Gbps Actual: 1Gbps<br>Interface: Ethernet3 - Auto-negotiation mismatch - Expected: success Actual: unknown<br>Interface: Ethernet3 - Data lanes count mismatch - Expected: 1 Actual: 0<br>Interface: Ethernet2 - Bandwidth mismatch - Expected: 2.5Gbps Actual: 1Gbps |
| s1-spine1 | Interfaces | VerifyInterfacesStatus | Verifies the operational states of specified interfaces to ensure they match expected configurations. | interface | failure | Port-Channel100 - Not configured<br>Ethernet49/1 - Not configured |
| s1-spine1 | Interfaces | VerifyIpVirtualRouterMac | Verifies the IP virtual router MAC address. | - | success | - |
| s1-spine1 | Interfaces | VerifyL2MTU | Verifies the global L2 MTU of all L2 interfaces. | - | failure | Interface: Ethernet3 - Incorrect MTU configured - Expected: 1500 Actual: 9214<br>Interface: Port-Channel5 - Incorrect MTU configured - Expected: 1500 Actual: 9214 |
| s1-spine1 | Interfaces | VerifyL3MTU | Verifies the global L3 MTU of all L3 interfaces. | - | failure | Interface: Ethernet2 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Ethernet1 - Incorrect MTU - Expected: 2500 Actual: 9214<br>Interface: Loopback0 - Incorrect MTU - Expected: 1500 Actual: 65535<br>Interface: Loopback1 - Incorrect MTU - Expected: 1500 Actual: 65535<br>Interface: Vlan1006 - Incorrect MTU - Expected: 1500 Actual: 9164<br>Interface: Vlan1199 - Incorrect MTU - Expected: 1500 Actual: 9164<br>Interface: Vlan4093 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan4094 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan3019 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan3009 - Incorrect MTU - Expected: 1500 Actual: 9214 |
| s1-spine1 | Interfaces | VerifyLACPInterfacesStatus | Verifies the Link Aggregation Control Protocol (LACP) status of the interface. | - | failure | Interface: Ethernet1 Port-Channel: Port-Channel100 - Not configured |
| s1-spine1 | Interfaces | VerifyLoopbackCount | Verifies the number of loopback interfaces and their status. | - | failure | Loopback interface(s) count mismatch: Expected 3 Actual: 2 |
| s1-spine1 | Interfaces | VerifyPortChannels | Verifies there are no inactive ports in all port channels. | - | success | - |
| s1-spine1 | Interfaces | VerifySVI | Verifies the status of all SVIs. | - | success | - |
| s1-spine1 | Interfaces | VerifyStormControlDrops | Verifies there are no interface storm-control drop counters. | - | skipped | VerifyStormControlDrops test is not supported on cEOSLab |
| s1-spine1 | ISIS | VerifyISISGracefulRestart | Verifies the IS-IS graceful restart feature. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS | VerifyISISInterfaceMode | Verifies IS-IS interfaces are running in the correct mode. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS | VerifyISISNeighborCount | Verifies the number of IS-IS neighbors per interface and level. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS | VerifyISISNeighborState | Verifies the health of IS-IS neighbors. | - | skipped | IS-IS not configured |
| s1-spine1 | LANZ | VerifyLANZ | Verifies if LANZ is enabled. | - | skipped | VerifyLANZ test is not supported on cEOSLab. |
| s1-spine1 | ISIS, Segment-Routing | VerifyISISSegmentRoutingAdjacencySegments | Verifies IS-IS segment routing adjacency segments. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS, Segment-Routing | VerifyISISSegmentRoutingDataplane | Verifies IS-IS segment routing data-plane configuration. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS, Segment-Routing | VerifyISISSegmentRoutingTunnels | Verify ISIS-SR tunnels computed by device. | - | skipped | IS-IS-SR not configured |
| s1-spine1 | LANZ | VerifyLANZ | Verifies if LANZ is enabled. | - | skipped | VerifyLANZ test is not supported on cEOSLab |
| s1-spine1 | Logging | VerifyLoggingAccounting | Verifies if AAA accounting logs are generated. | - | success | - |
| s1-spine1 | Logging | VerifyLoggingEntries | Verifies that the expected log string is present in the last specified log messages. | - | failure | Pattern: `.*ACCOUNTING-5-EXEC: cvpadmin ssh.*` - Not found in last 30 alerts log entries<br>Pattern: `.*SPANTREE-6-INTERFACE_ADD:.*` - Not found in last 10 critical log entries |
| s1-spine1 | Logging | VerifyLoggingErrors | Verifies there are no syslog messages with a severity of ERRORS or higher. | - | failure | Device has reported syslog messages with a severity of ERRORS or higher:<br>Apr 29 08:01:27 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: sent to neighbor 10.100.4.5 (VRF data AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> Apr 29 08:01:27 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: sent to neighbor 10.100.4.5 (VRF guest AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> Apr 29 08:01:29 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: received from neighbor 10.100.4.5 (VRF guest AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> <br> |
| s1-spine1 | Logging | VerifyLoggingHostname | Verifies if logs are generated with the device FQDN. | - | failure | Logs are not generated with the device FQDN |
| s1-spine1 | Logging | VerifyLoggingHosts | Verifies logging hosts (syslog servers) for a specified VRF. | - | failure | Syslog servers 1.1.1.1, 2.2.2.2 are not configured in VRF default |
| s1-spine1 | Logging | VerifyLoggingLogsGeneration | Verifies if logs are generated. | - | success | - |
| s1-spine1 | Logging | VerifyLoggingPersistent | Verifies if logging persistent is enabled and logs are saved in flash. | - | failure | Persistent logging is disabled |
| s1-spine1 | Logging | VerifyLoggingSourceIntf | Verifies logging source-interface for a specified VRF. | - | failure | Source-interface: Management0 VRF: default - Not configured |
| s1-spine1 | Logging | VerifyLoggingTimestamp | Verifies if logs are generated with the appropriate timestamp. | - | failure | Logs are not generated with the appropriate timestamp format |
| s1-spine1 | Logging | VerifySyslogLogging | Verifies if syslog logging is enabled. | - | success | - |
| s1-spine1 | MLAG | VerifyMlagConfigSanity | Verifies there are no MLAG config-sanity inconsistencies. | - | success | - |
| s1-spine1 | MLAG | VerifyMlagDualPrimary | Verifies the MLAG dual-primary detection parameters. | - | failure | Dual-primary detection is disabled |
| s1-spine1 | OSPF | VerifyOSPFMaxLSA | Verifies all OSPF instances did not cross the maximum LSA threshold. | - | skipped | No OSPF instance found. |
| s1-spine1 | Path-Selection | VerifyPathsHealth | Verifies the path and telemetry state of all paths under router path-selection. | - | skipped | VerifyPathsHealth test is not supported on cEOSLab. |
| s1-spine1 | Profiles | VerifyTcamProfile | Verifies the device TCAM profile. | - | skipped | VerifyTcamProfile test is not supported on cEOSLab. |
| s1-spine1 | PTP | VerifyPtpGMStatus | Verifies that the device is locked to a valid PTP Grandmaster. | - | skipped | VerifyPtpGMStatus test is not supported on cEOSLab. |
| s1-spine1 | MLAG | VerifyMlagInterfaces | Verifies there are no inactive or active-partial MLAG ports. | - | success | - |
| s1-spine1 | MLAG | VerifyMlagPrimaryPriority | Verifies the configuration of the MLAG primary priority. | - | failure | MLAG primary priority mismatch - Expected: 3276 Actual: 32767 |
| s1-spine1 | MLAG | VerifyMlagReloadDelay | Verifies the reload-delay parameters of the MLAG configuration. | - | success | - |
| s1-spine1 | MLAG | VerifyMlagStatus | Verifies the health status of the MLAG configuration. | - | success | - |
| s1-spine1 | Multicast | VerifyIGMPSnoopingGlobal | Verifies the IGMP snooping global status. | - | success | - |
| s1-spine1 | Multicast | VerifyIGMPSnoopingVlans | Verifies the IGMP snooping status for the provided VLANs. | - | failure | VLAN10 - Incorrect IGMP state - Expected: disabled Actual: enabled<br>Supplied vlan 12 is not present on the device |
| s1-spine1 | OSPF | VerifyOSPFMaxLSA | Verifies all OSPF instances did not cross the maximum LSA threshold. | - | skipped | OSPF not configured |
| s1-spine1 | OSPF | VerifyOSPFNeighborCount | Verifies the number of OSPF neighbors in FULL state is the one we expect. | - | skipped | OSPF not configured |
| s1-spine1 | OSPF | VerifyOSPFNeighborState | Verifies all OSPF neighbors are in FULL state. | - | skipped | OSPF not configured |
| s1-spine1 | Path-Selection | VerifyPathsHealth | Verifies the path and telemetry state of all paths under router path-selection. | - | skipped | VerifyPathsHealth test is not supported on cEOSLab |
| s1-spine1 | Path-Selection | VerifySpecificPath | Verifies the DPS path and telemetry state of an IPv4 peer. | - | skipped | VerifySpecificPath test is not supported on cEOSLab |
| s1-spine1 | Profiles | VerifyTcamProfile | Verifies the device TCAM profile. | - | skipped | VerifyTcamProfile test is not supported on cEOSLab |
| s1-spine1 | Profiles | VerifyUnifiedForwardingTableMode | Verifies the device is using the expected UFT mode. | - | skipped | VerifyUnifiedForwardingTableMode test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpGMStatus | Verifies that the device is locked to a valid PTP Grandmaster. | - | skipped | VerifyPtpGMStatus test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpLockStatus | Verifies that the device was locked to the upstream PTP GM in the last minute. | - | skipped | VerifyPtpLockStatus test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpModeStatus | Verifies that the device is configured as a PTP Boundary Clock. | - | skipped | VerifyPtpModeStatus test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpOffset | Verifies that the PTP timing offset is within +/- 1000ns from the master clock. | - | skipped | VerifyPtpOffset test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpPortModeStatus | Verifies the PTP interfaces state. | - | skipped | VerifyPtpPortModeStatus test is not supported on cEOSLab |
| s1-spine1 | Routing | VerifyIPv4RouteNextHops | Verifies the next-hops of the IPv4 prefixes. | - | success | - |
| s1-spine1 | Security | VerifyBannerLogin | Verifies the login banner of a device. | - | failure | Expected '# Copyright (c) 2023-2024 Arista Networks, Inc.<br># Use of this source code is governed by the Apache License 2.0<br># that can be found in the LICENSE file.<br>' as the login banner, but found '' instead. |
| s1-spine1 | Security | VerifyBannerMotd | Verifies the motd banner of a device. | - | failure | Expected '# Copyright (c) 2023-2024 Arista Networks, Inc.<br># Use of this source code is governed by the Apache License 2.0<br># that can be found in the LICENSE file.<br>' as the motd banner, but found '' instead. |
| s1-spine1 | Routing | VerifyIPv4RouteType | Verifies the route-type of the IPv4 prefixes. | - | failure | Prefix: 10.100.0.12/31 VRF: default - Route not found<br>Prefix: 10.100.1.5/32 VRF: default - Incorrect route type - Expected: iBGP Actual: connected |
| s1-spine1 | Routing | VerifyRoutingProtocolModel | Verifies the configured routing protocol model. | - | success | - |
| s1-spine1 | Routing | VerifyRoutingStatus | Verifies the routing status for IPv4/IPv6 unicast, multicast, and IPv6 interfaces (RFC5549). | - | failure | IPv6 unicast routing enabled status mismatch - Expected: True Actual: False |
| s1-spine1 | Routing | VerifyRoutingTableEntry | Verifies that the provided routes are present in the routing table of a specified VRF. | - | failure | The following route(s) are missing from the routing table of VRF default: 10.1.0.1, 10.1.0.2 |
| s1-spine1 | Routing | VerifyRoutingTableSize | Verifies the size of the IP routing table of the default VRF. | - | failure | Routing table routes are outside the routes range - Expected: 2 <= to >= 20 Actual: 35 |
| s1-spine1 | Security | VerifyAPIHttpStatus | Verifies if eAPI HTTP server is disabled globally. | - | success | - |
| s1-spine1 | Security | VerifyAPIHttpsSSL | Verifies if the eAPI has a valid SSL profile. | - | failure | eAPI HTTPS server SSL profile default is not configured |
| s1-spine1 | Security | VerifyAPIIPv4Acl | Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF. | - | failure | VRF: default - eAPI IPv4 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifyAPIIPv6Acl | Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF. | - | failure | VRF: default - eAPI IPv6 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifyAPISSLCertificate | Verifies the eAPI SSL certificate expiry, common subject name, encryption algorithm and key size. | - | success | - |
| s1-spine1 | Security | VerifyBannerLogin | Verifies the login banner of a device. | - | failure | Login banner is not configured |
| s1-spine1 | Security | VerifyBannerMotd | Verifies the motd banner of a device. | - | failure | MOTD banner is not configured |
| s1-spine1 | Security | VerifyHardwareEntropy | Verifies hardware entropy generation is enabled on device. | - | failure | Hardware entropy generation is disabled |
| s1-spine1 | Security | VerifyIPSecConnHealth | Verifies all IPv4 security connections. | - | failure | No IPv4 security connection configured |
| s1-spine1 | Security | VerifyIPv4ACL | Verifies the configuration of IPv4 ACLs. | - | failure | ACL name: LabTest - Not configured |
| s1-spine1 | Security | VerifySSHIPv4Acl | Verifies if the SSHD agent has IPv4 ACL(s) configured. | - | failure | VRF: default - SSH IPv4 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifySSHIPv6Acl | Verifies if the SSHD agent has IPv6 ACL(s) configured. | - | failure | VRF: default - SSH IPv6 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifySSHStatus | Verifies if the SSHD agent is disabled in the default VRF. | - | failure | SSHD status for Default VRF is enabled |
| s1-spine1 | Security | VerifySpecificIPSecConn | Verifies the IPv4 security connections. | - | failure | Peer: 10.255.0.1 VRF: default - Not configured<br>Peer: 10.255.0.2 VRF: default - Not configured |
| s1-spine1 | Security | VerifyTelnetStatus | Verifies if Telnet is disabled in the default VRF. | - | success | - |
| s1-spine1 | Services | VerifyDNSLookup | Verifies the DNS name to IP address resolution. | - | success | - |
| s1-spine1 | Services | VerifyDNSServers | Verifies if the DNS (Domain Name Service) servers are correctly configured. | - | failure | Server 10.14.0.1 VRF: default Priority: 1 - Not configured<br>Server 10.14.0.11 VRF: MGMT Priority: 0 - Not configured |
| s1-spine1 | Services | VerifyErrdisableRecovery | Verifies the error disable recovery functionality. | - | failure | Reason: acl Status: Enabled Interval: 30 - Incorrect configuration - Status: Disabled Interval: 300<br>Reason: bpduguard Status: Enabled Interval: 30 - Incorrect configuration - Status: Disabled Interval: 300 |
| s1-spine1 | Services | VerifyHostname | Verifies the hostname of a device. | - | failure | Incorrect Hostname - Expected: s1-spine1 Actual: leaf1-dc1 |
| s1-spine1 | SNMP | VerifySnmpContact | Verifies the SNMP contact of a device. | - | failure | SNMP contact is not configured. |
| s1-spine1 | SNMP | VerifySnmpContact | Verifies the SNMP contact of a device. | - | failure | SNMP contact is not configured |
| s1-spine1 | SNMP | VerifySnmpErrorCounters | Verifies the SNMP error counters. | - | failure | SNMP counters not found |
| s1-spine1 | SNMP | VerifySnmpGroup | Verifies the SNMP group configurations for specified version(s). | - | failure | Group: Group1 Version: v1 - Not configured<br>Group: Group2 Version: v3 - Not configured |
| s1-spine1 | SNMP | VerifySnmpHostLogging | Verifies SNMP logging configurations. | - | failure | SNMP logging is disabled |
| s1-spine1 | SNMP | VerifySnmpIPv4Acl | Verifies if the SNMP agent has IPv4 ACL(s) configured. | - | failure | VRF: default - Incorrect SNMP IPv4 ACL(s) - Expected: 3 Actual: 0 |
| s1-spine1 | SNMP | VerifySnmpIPv6Acl | Verifies if the SNMP agent has IPv6 ACL(s) configured. | - | failure | VRF: default - Incorrect SNMP IPv6 ACL(s) - Expected: 3 Actual: 0 |
| s1-spine1 | SNMP | VerifySnmpLocation | Verifies the SNMP location of a device. | - | failure | SNMP location is not configured |
| s1-spine1 | SNMP | VerifySnmpNotificationHost | Verifies the SNMP notification host(s) (SNMP manager) configurations. | - | failure | No SNMP host is configured |
| s1-spine1 | SNMP | VerifySnmpPDUCounters | Verifies the SNMP PDU counters. | - | failure | SNMP counters not found |
| s1-spine1 | SNMP | VerifySnmpSourceInterface | Verifies SNMP source interfaces. | - | failure | SNMP source interface(s) not configured |
| s1-spine1 | SNMP | VerifySnmpStatus | Verifies if the SNMP agent is enabled. | - | failure | VRF: default - SNMP agent disabled |
| s1-spine1 | SNMP | VerifySnmpUser | Verifies the SNMP user configurations. | - | failure | User: test Group: test_group Version: v3 - Not found |
| s1-spine1 | Software | VerifyEOSExtensions | Verifies that all EOS extensions installed on the device are enabled for boot persistence. | - | success | - |
| s1-spine1 | Software | VerifyEOSVersion | Verifies the EOS version of the device. | - | failure | EOS version mismatch - Actual: 4.31.0F-33804048.4310F (engineering build) not in Expected: 4.25.4M, 4.26.1F |
| s1-spine1 | Software | VerifyTerminAttrVersion | Verifies the TerminAttr version of the device. | - | failure | TerminAttr version mismatch - Actual: v1.29.0 not in Expected: v1.13.6, v1.8.0 |
| s1-spine1 | STP | VerifySTPBlockedPorts | Verifies there is no STP blocked ports. | - | success | - |
| s1-spine1 | STUN | VerifyStunClient | (Deprecated) Verifies the translation for a source address on a STUN client. | - | failure | Client 172.18.3.2 Port: 4500 - STUN client translation not found. |
| s1-spine1 | STUN | VerifyStunClientTranslation | Verifies the translation for a source address on a STUN client. | - | failure | Client 172.18.3.2 Port: 4500 - STUN client translation not found.<br>Client 100.64.3.2 Port: 4500 - STUN client translation not found. |
| s1-spine1 | System | VerifyNTPAssociations | Verifies the Network Time Protocol (NTP) associations. | - | failure | NTP Server: 1.1.1.1 Preferred: True Stratum: 1 - Not configured<br>NTP Server: 2.2.2.2 Preferred: False Stratum: 2 - Not configured<br>NTP Server: 3.3.3.3 Preferred: False Stratum: 2 - Not configured |
| s1-spine1 | STP | VerifySTPCounters | Verifies there is no errors in STP BPDU packets. | - | success | - |
| s1-spine1 | STP | VerifySTPDisabledVlans | Verifies the STP disabled VLAN(s). | - | failure | VLAN: 6 - Not configured |
| s1-spine1 | STP | VerifySTPForwardingPorts | Verifies that all interfaces are forwarding for a provided list of VLAN(s). | - | success | - |
| s1-spine1 | STP | VerifySTPMode | Verifies the configured STP mode for a provided list of VLAN(s). | - | failure | VLAN 10 - Incorrect STP mode - Expected: rapidPvst Actual: mstp<br>VLAN 20 - Incorrect STP mode - Expected: rapidPvst Actual: mstp |
| s1-spine1 | STP | VerifySTPRootPriority | Verifies the STP root priority for a provided list of VLAN or MST instance ID(s). | - | failure | Instance: MST10 - Not configured<br>Instance: MST20 - Not configured |
| s1-spine1 | STP | VerifyStpTopologyChanges | Verifies the number of changes across all interfaces in the Spanning Tree Protocol (STP) topology is below a threshold. | - | success | - |
| s1-spine1 | STUN | VerifyStunClient | (Deprecated) Verifies the translation for a source address on a STUN client. | - | failure | Client 172.18.3.2 Port: 4500 - STUN client translation not found |
| s1-spine1 | STUN | VerifyStunClientTranslation | Verifies the translation for a source address on a STUN client. | - | failure | Client 172.18.3.2 Port: 4500 - STUN client translation not found<br>Client 100.64.3.2 Port: 4500 - STUN client translation not found |
| s1-spine1 | STUN | VerifyStunServer | Verifies the STUN server status is enabled and running. | - | failure | STUN server status is disabled and not running |
| s1-spine1 | System | VerifyAgentLogs | Verifies there are no agent crash reports. | - | success | - |
| s1-spine1 | System | VerifyCPUUtilization | Verifies whether the CPU utilization is below 75%. | - | success | - |
| s1-spine1 | System | VerifyCoredump | Verifies there are no core dump files. | - | success | - |
| s1-spine1 | System | VerifyFileSystemUtilization | Verifies that no partition is utilizing more than 75% of its disk space. | - | success | - |
| s1-spine1 | System | VerifyMaintenance | Verifies that the device is not currently under or entering maintenance. | - | success | - |
| s1-spine1 | System | VerifyNTP | Verifies if NTP is synchronised. | - | success | - |
| s1-spine1 | System | VerifyReloadCause | Verifies the last reload cause of the device. | reload-cause | success | - |
| s1-spine1 | System | VerifyUptime | Verifies the device uptime. | - | success | - |
| s1-spine1 | VLAN | VerifyDynamicVlanSource | Verifies dynamic VLAN allocation for specified VLAN sources. | - | failure | Dynamic VLAN source(s) exist but have no VLANs allocated: mlagsync |
| s1-spine1 | VXLAN | VerifyVxlan1ConnSettings | Verifies the interface vxlan1 source interface and UDP port. | - | success | - |
| s1-spine1 | VLAN | VerifyVlanInternalPolicy | Verifies the VLAN internal allocation policy and the range of VLANs. | - | failure | VLAN internal allocation policy: ascending - Incorrect end VLAN id configured - Expected: 4094 Actual: 1199 |
| s1-spine1 | VLAN | VerifyVlanStatus | Verifies the administrative status of specified VLANs. | - | failure | VLAN: Vlan10 - Incorrect administrative status - Expected: suspended Actual: active |
| s1-spine1 | VXLAN | VerifyVxlan1ConnSettings | Verifies Vxlan1 source interface and UDP port. | - | success | - |
| s1-spine1 | VXLAN | VerifyVxlan1Interface | Verifies the Vxlan1 interface status. | - | success | - |
| s1-spine1 | VXLAN | VerifyVxlanConfigSanity | Verifies there are no VXLAN config-sanity inconsistencies. | - | success | - |
| s1-spine1 | VXLAN | VerifyVxlanVniBinding | Verifies the VNI-VLAN, VNI-VRF bindings of the Vxlan1 interface. | - | failure | Interface: Vxlan1 VNI: 500 - Binding not found |
| s1-spine1 | VXLAN | VerifyVxlanVtep | Verifies Vxlan1 VTEP peers. | - | failure | The following VTEP peer(s) are missing from the Vxlan1 interface: 10.1.1.5, 10.1.1.6<br>Unexpected VTEP peer(s) on Vxlan1 interface: 10.100.2.3 |

View file

@ -0,0 +1,355 @@
# ANTA Report
**Table of Contents:**
- [ANTA Report](#anta-report)
- [Test Results Summary](#test-results-summary)
- [Summary Totals](#summary-totals)
- [Summary Totals Device Under Test](#summary-totals-device-under-test)
- [Summary Totals Per Category](#summary-totals-per-category)
- [Test Results](#test-results)
## Test Results Summary
### Summary Totals
| Total Tests | Total Tests Success | Total Tests Skipped | Total Tests Failure | Total Tests Error |
| ----------- | ------------------- | ------------------- | ------------------- | ------------------|
| 181 | 43 | 34 | 103 | 1 |
### Summary Totals Device Under Test
| Device Under Test | Total Tests | Tests Success | Tests Skipped | Tests Failure | Tests Error | Categories Skipped | Categories Failed |
| ------------------| ----------- | ------------- | ------------- | ------------- | ----------- | -------------------| ------------------|
| s1-spine1 | 181 | 43 | 34 | 103 | 1 | AVT, Field Notices, Flow Tracking, Hardware, ISIS, Interfaces, LANZ, OSPF, PTP, Path-Selection, Profiles, Segment-Routing | AAA, BFD, BGP, Configuration, Connectivity, Cvx, Greent, Interfaces, Logging, MLAG, Multicast, Routing, SNMP, STP, STUN, Security, Services, Software, VLAN, VXLAN |
### Summary Totals Per Category
| Test Category | Total Tests | Tests Success | Tests Skipped | Tests Failure | Tests Error |
| ------------- | ----------- | ------------- | ------------- | ------------- | ----------- |
| AAA | 7 | 0 | 0 | 7 | 0 |
| AVT | 3 | 0 | 3 | 0 | 0 |
| BFD | 4 | 1 | 0 | 3 | 0 |
| BGP | 25 | 3 | 0 | 21 | 1 |
| Configuration | 3 | 1 | 0 | 2 | 0 |
| Connectivity | 2 | 1 | 0 | 1 | 0 |
| Cvx | 5 | 0 | 0 | 5 | 0 |
| Field Notices | 2 | 0 | 2 | 0 | 0 |
| Flow Tracking | 1 | 0 | 1 | 0 | 0 |
| Greent | 2 | 0 | 0 | 2 | 0 |
| Hardware | 7 | 0 | 7 | 0 | 0 |
| Interfaces | 16 | 7 | 1 | 8 | 0 |
| ISIS | 7 | 0 | 7 | 0 | 0 |
| LANZ | 1 | 0 | 1 | 0 | 0 |
| Logging | 10 | 3 | 0 | 7 | 0 |
| MLAG | 6 | 4 | 0 | 2 | 0 |
| Multicast | 2 | 1 | 0 | 1 | 0 |
| OSPF | 3 | 0 | 3 | 0 | 0 |
| Path-Selection | 2 | 0 | 2 | 0 | 0 |
| Profiles | 2 | 0 | 2 | 0 | 0 |
| PTP | 5 | 0 | 5 | 0 | 0 |
| Routing | 6 | 2 | 0 | 4 | 0 |
| Security | 15 | 3 | 0 | 12 | 0 |
| Segment-Routing | 3 | 0 | 3 | 0 | 0 |
| Services | 4 | 1 | 0 | 3 | 0 |
| SNMP | 12 | 0 | 0 | 12 | 0 |
| Software | 3 | 1 | 0 | 2 | 0 |
| STP | 7 | 4 | 0 | 3 | 0 |
| STUN | 3 | 0 | 0 | 3 | 0 |
| System | 8 | 8 | 0 | 0 | 0 |
| VLAN | 3 | 0 | 0 | 3 | 0 |
| VXLAN | 5 | 3 | 0 | 2 | 0 |
## Failed Test Results Summary
| Device Under Test | Categories | Test | Description | Custom Field | Result | Messages |
| ----------------- | ---------- | ---- | ----------- | ------------ | ------ | -------- |
| s1-spine1 | AAA | VerifyAcctConsoleMethods | Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x). | - | failure | AAA console accounting is not configured for commands, exec, system, dot1x |
| s1-spine1 | AAA | VerifyAcctDefaultMethods | Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x). | - | failure | AAA default accounting is not configured for commands, exec, system, dot1x |
| s1-spine1 | AAA | VerifyAuthenMethods | Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x). | - | failure | AAA authentication methods are not configured for login console |
| s1-spine1 | AAA | VerifyAuthzMethods | Verifies the AAA authorization method lists for different authorization types (commands, exec). | - | failure | AAA authorization methods local, none, logging are not matching for commands, exec |
| s1-spine1 | AAA | VerifyTacacsServerGroups | Verifies if the provided TACACS server group(s) are configured. | - | failure | No TACACS server group(s) are configured |
| s1-spine1 | AAA | VerifyTacacsServers | Verifies TACACS servers are configured for a specified VRF. | - | failure | No TACACS servers are configured |
| s1-spine1 | AAA | VerifyTacacsSourceIntf | Verifies TACACS source-interface for a specified VRF. | - | failure | VRF: MGMT Source Interface: Management0 - Not configured |
| s1-spine1 | BFD | VerifyBFDPeersIntervals | Verifies the timers of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.8 VRF: default - Not found<br>Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BFD | VerifyBFDPeersRegProtocols | Verifies the registered routing protocol of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BFD | VerifyBFDSpecificPeers | Verifies the state of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.8 VRF: default - Not found<br>Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPAdvCommunities | Verifies the advertised communities of BGP peers. | - | failure | Peer: 172.30.11.17 VRF: default - Not found<br>Peer: 172.30.11.21 VRF: MGMT - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPExchangedRoutes | Verifies the advertised and received routes of BGP IPv4 peer(s). | - | failure | Peer: 172.30.255.5 VRF: default Advertised route: 192.0.254.5/32 - Not found<br>Peer: 172.30.255.5 VRF: default Received route: 192.0.255.4/32 - Not found<br>Peer: 172.30.255.1 VRF: default Advertised route: 192.0.255.1/32 - Not found<br>Peer: 172.30.255.1 VRF: default Advertised route: 192.0.254.5/32 - Not found |
| s1-spine1 | BGP | VerifyBGPNlriAcceptance | Verifies that all received NLRI are accepted for all AFI/SAFI configured for BGP peers. | - | failure | Peer: 10.100.0.128 VRF: default - Not found<br>Peer: 2001:db8:1::2 VRF: default - Not found<br>Peer: fe80::2%Et1 VRF: default - Not found<br>Peer: fe80::2%Et1 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPPeerASNCap | Verifies the four octet ASN capability of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerCount | Verifies the count of BGP peers for given address families. | - | failure | AFI: ipv4 SAFI: unicast VRF: PROD - VRF not configured<br>AFI: ipv4 SAFI: multicast VRF: DEV - VRF not configured |
| s1-spine1 | BGP | VerifyBGPPeerDropStats | Verifies BGP NLRI drop statistics of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerGroup | Verifies BGP peer group of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerMD5Auth | Verifies the MD5 authentication and state of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.5 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: default - Session does not have MD5 authentication enabled |
| s1-spine1 | BGP | VerifyBGPPeerMPCaps | Verifies the multiprotocol capabilities of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: default - ipv4MplsLabels not found<br>Interface: Ethernet1 VRF: default - ipv4MplsVpn not found |
| s1-spine1 | BGP | VerifyBGPPeerRouteLimit | Verifies maximum routes and warning limit for BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerRouteRefreshCap | Verifies the route refresh capabilities of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerSession | Verifies the session state of BGP peers. | - | failure | Peer: 10.1.0.1 VRF: default - Not found<br>Peer: 10.1.0.2 VRF: default - Not found<br>Peer: 10.1.255.2 VRF: DEV - Not found<br>Peer: 10.1.255.4 VRF: DEV - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Vlan3499 VRF: PROD - Not found |
| s1-spine1 | BGP | VerifyBGPPeerSessionRibd | Verifies the session state of BGP peers. | - | failure | Peer: 10.1.0.1 VRF: default - Not found<br>Peer: 10.1.255.4 VRF: DEV - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerTtlMultiHops | Verifies BGP TTL and max-ttl-hops count for BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.2 VRF: test - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerUpdateErrors | Verifies BGP update error counters of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeersHealth | Verifies the health of BGP peers for given address families. | - | failure | AFI: ipv6 SAFI: unicast VRF: DEV - VRF not configured |
| s1-spine1 | BGP | VerifyBGPSpecificPeers | Verifies the health of specific BGP peer(s) for given address families. | - | failure | AFI: evpn Peer: 10.1.0.1 - Not configured<br>AFI: evpn Peer: 10.1.0.2 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.254.1 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.0 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.2 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.4 - Not configured |
| s1-spine1 | BGP | VerifyBGPTimers | Verifies the timers of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.5 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBgpRouteMaps | Verifies BGP inbound and outbound route-maps of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyEVPNType2Route | Verifies the EVPN Type-2 routes for a given IPv4 or MAC address and VNI. | - | failure | Address: 192.168.20.102 VNI: 10020 - No EVPN Type-2 route<br>Address: aa:c1:ab:5d:b4:1e VNI: 10010 - No EVPN Type-2 route |
| s1-spine1 | BGP | VerifyEVPNType5Routes | Verifies EVPN Type-5 routes for given IP prefixes and VNIs. | - | failure | Prefix: 192.168.10.0/24 VNI: 10 - No EVPN Type-5 routes found<br>Prefix: 192.168.20.0/24 VNI: 20 - No EVPN Type-5 routes found<br>Prefix: 192.168.30.0/24 VNI: 30 - No EVPN Type-5 routes found<br>Prefix: 192.168.40.0/24 VNI: 40 - No EVPN Type-5 routes found |
| s1-spine1 | Configuration | VerifyRunningConfigDiffs | Verifies there is no difference between the running-config and the startup-config. | - | failure | --- flash:/startup-config<br>+++ system:/running-config<br>@@ -18,6 +18,7 @@<br> hostname leaf1-dc1<br> ip name-server vrf MGMT 10.14.0.1<br> dns domain fun.aristanetworks.com<br>+ip host vitthal 192.168.66.220<br> !<br> platform tfa<br> personality arfa<br> |
| s1-spine1 | Configuration | VerifyRunningConfigLines | Search the Running-Config for the given RegEx patterns. | - | failure | Following patterns were not found: '^enable password.*$', 'bla bla' |
| s1-spine1 | Connectivity | VerifyLLDPNeighbors | Verifies the connection status of the specified LLDP (Link Layer Discovery Protocol) neighbors. | - | failure | Port: Ethernet1 Neighbor: DC1-SPINE1 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: spine1-dc1.fun.aristanetworks.com/Ethernet3<br>Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: spine2-dc1.fun.aristanetworks.com/Ethernet3 |
| s1-spine1 | Cvx | VerifyActiveCVXConnections | Verifies the number of active CVX Connections. | - | failure | 'show cvx connections brief' failed on s1-spine1: Unavailable command (controller not ready) (at token 2: 'connections') |
| s1-spine1 | Cvx | VerifyCVXClusterStatus | Verifies the CVX Server Cluster status. | - | failure | CVX Server status is not enabled<br>CVX Server is not a cluster |
| s1-spine1 | Cvx | VerifyManagementCVX | Verifies the management CVX global status. | - | failure | Management CVX status is not valid: Expected: enabled Actual: disabled |
| s1-spine1 | Cvx | VerifyMcsClientMounts | Verify if all MCS client mounts are in mountStateMountComplete. | - | failure | MCS Client mount states are not present |
| s1-spine1 | Cvx | VerifyMcsServerMounts | Verify if all MCS server mounts are in a MountComplete state. | - | failure | 'show cvx mounts' failed on s1-spine1: Unavailable command (controller not ready) (at token 2: 'mounts') |
| s1-spine1 | Greent | VerifyGreenT | Verifies if a GreenT policy other than the default is created. | - | failure | No GreenT policy is created |
| s1-spine1 | Greent | VerifyGreenTCounters | Verifies if the GreenT counters are incremented. | - | failure | GreenT counters are not incremented |
| s1-spine1 | Interfaces | VerifyIPProxyARP | Verifies if Proxy ARP is enabled. | - | failure | Interface: Ethernet1 - Proxy-ARP disabled<br>Interface: Ethernet2 - Proxy-ARP disabled |
| s1-spine1 | Interfaces | VerifyInterfaceIPv4 | Verifies the interface IPv4 addresses. | - | failure | Interface: Ethernet2 - IP address mismatch - Expected: 172.30.11.1/31 Actual: 10.100.0.11/31<br>Interface: Ethernet2 - Secondary IP address is not configured |
| s1-spine1 | Interfaces | VerifyInterfacesSpeed | Verifies the speed, lanes, auto-negotiation status, and mode as full duplex for interfaces. | - | failure | Interface: Ethernet2 - Bandwidth mismatch - Expected: 10.0Gbps Actual: 1Gbps<br>Interface: Ethernet3 - Bandwidth mismatch - Expected: 100.0Gbps Actual: 1Gbps<br>Interface: Ethernet3 - Auto-negotiation mismatch - Expected: success Actual: unknown<br>Interface: Ethernet3 - Data lanes count mismatch - Expected: 1 Actual: 0<br>Interface: Ethernet2 - Bandwidth mismatch - Expected: 2.5Gbps Actual: 1Gbps |
| s1-spine1 | Interfaces | VerifyInterfacesStatus | Verifies the operational states of specified interfaces to ensure they match expected configurations. | interface | failure | Port-Channel100 - Not configured<br>Ethernet49/1 - Not configured |
| s1-spine1 | Interfaces | VerifyL2MTU | Verifies the global L2 MTU of all L2 interfaces. | - | failure | Interface: Ethernet3 - Incorrect MTU configured - Expected: 1500 Actual: 9214<br>Interface: Port-Channel5 - Incorrect MTU configured - Expected: 1500 Actual: 9214 |
| s1-spine1 | Interfaces | VerifyL3MTU | Verifies the global L3 MTU of all L3 interfaces. | - | failure | Interface: Ethernet2 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Ethernet1 - Incorrect MTU - Expected: 2500 Actual: 9214<br>Interface: Loopback0 - Incorrect MTU - Expected: 1500 Actual: 65535<br>Interface: Loopback1 - Incorrect MTU - Expected: 1500 Actual: 65535<br>Interface: Vlan1006 - Incorrect MTU - Expected: 1500 Actual: 9164<br>Interface: Vlan1199 - Incorrect MTU - Expected: 1500 Actual: 9164<br>Interface: Vlan4093 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan4094 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan3019 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan3009 - Incorrect MTU - Expected: 1500 Actual: 9214 |
| s1-spine1 | Interfaces | VerifyLACPInterfacesStatus | Verifies the Link Aggregation Control Protocol (LACP) status of the interface. | - | failure | Interface: Ethernet1 Port-Channel: Port-Channel100 - Not configured |
| s1-spine1 | Interfaces | VerifyLoopbackCount | Verifies the number of loopback interfaces and their status. | - | failure | Loopback interface(s) count mismatch: Expected 3 Actual: 2 |
| s1-spine1 | Logging | VerifyLoggingEntries | Verifies that the expected log string is present in the last specified log messages. | - | failure | Pattern: `.*ACCOUNTING-5-EXEC: cvpadmin ssh.*` - Not found in last 30 alerts log entries<br>Pattern: `.*SPANTREE-6-INTERFACE_ADD:.*` - Not found in last 10 critical log entries |
| s1-spine1 | Logging | VerifyLoggingErrors | Verifies there are no syslog messages with a severity of ERRORS or higher. | - | failure | Device has reported syslog messages with a severity of ERRORS or higher:<br>Apr 29 08:01:27 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: sent to neighbor 10.100.4.5 (VRF data AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> Apr 29 08:01:27 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: sent to neighbor 10.100.4.5 (VRF guest AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> Apr 29 08:01:29 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: received from neighbor 10.100.4.5 (VRF guest AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> <br> |
| s1-spine1 | Logging | VerifyLoggingHostname | Verifies if logs are generated with the device FQDN. | - | failure | Logs are not generated with the device FQDN |
| s1-spine1 | Logging | VerifyLoggingHosts | Verifies logging hosts (syslog servers) for a specified VRF. | - | failure | Syslog servers 1.1.1.1, 2.2.2.2 are not configured in VRF default |
| s1-spine1 | Logging | VerifyLoggingPersistent | Verifies if logging persistent is enabled and logs are saved in flash. | - | failure | Persistent logging is disabled |
| s1-spine1 | Logging | VerifyLoggingSourceIntf | Verifies logging source-interface for a specified VRF. | - | failure | Source-interface: Management0 VRF: default - Not configured |
| s1-spine1 | Logging | VerifyLoggingTimestamp | Verifies if logs are generated with the appropriate timestamp. | - | failure | Logs are not generated with the appropriate timestamp format |
| s1-spine1 | MLAG | VerifyMlagDualPrimary | Verifies the MLAG dual-primary detection parameters. | - | failure | Dual-primary detection is disabled |
| s1-spine1 | MLAG | VerifyMlagPrimaryPriority | Verifies the configuration of the MLAG primary priority. | - | failure | MLAG primary priority mismatch - Expected: 3276 Actual: 32767 |
| s1-spine1 | Multicast | VerifyIGMPSnoopingVlans | Verifies the IGMP snooping status for the provided VLANs. | - | failure | VLAN10 - Incorrect IGMP state - Expected: disabled Actual: enabled<br>Supplied vlan 12 is not present on the device |
| s1-spine1 | Routing | VerifyIPv4RouteType | Verifies the route-type of the IPv4 prefixes. | - | failure | Prefix: 10.100.0.12/31 VRF: default - Route not found<br>Prefix: 10.100.1.5/32 VRF: default - Incorrect route type - Expected: iBGP Actual: connected |
| s1-spine1 | Routing | VerifyRoutingStatus | Verifies the routing status for IPv4/IPv6 unicast, multicast, and IPv6 interfaces (RFC5549). | - | failure | IPv6 unicast routing enabled status mismatch - Expected: True Actual: False |
| s1-spine1 | Routing | VerifyRoutingTableEntry | Verifies that the provided routes are present in the routing table of a specified VRF. | - | failure | The following route(s) are missing from the routing table of VRF default: 10.1.0.1, 10.1.0.2 |
| s1-spine1 | Routing | VerifyRoutingTableSize | Verifies the size of the IP routing table of the default VRF. | - | failure | Routing table routes are outside the routes range - Expected: 2 <= to >= 20 Actual: 35 |
| s1-spine1 | Security | VerifyAPIHttpsSSL | Verifies if the eAPI has a valid SSL profile. | - | failure | eAPI HTTPS server SSL profile default is not configured |
| s1-spine1 | Security | VerifyAPIIPv4Acl | Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF. | - | failure | VRF: default - eAPI IPv4 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifyAPIIPv6Acl | Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF. | - | failure | VRF: default - eAPI IPv6 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifyBannerLogin | Verifies the login banner of a device. | - | failure | Login banner is not configured |
| s1-spine1 | Security | VerifyBannerMotd | Verifies the motd banner of a device. | - | failure | MOTD banner is not configured |
| s1-spine1 | Security | VerifyHardwareEntropy | Verifies hardware entropy generation is enabled on device. | - | failure | Hardware entropy generation is disabled |
| s1-spine1 | Security | VerifyIPSecConnHealth | Verifies all IPv4 security connections. | - | failure | No IPv4 security connection configured |
| s1-spine1 | Security | VerifyIPv4ACL | Verifies the configuration of IPv4 ACLs. | - | failure | ACL name: LabTest - Not configured |
| s1-spine1 | Security | VerifySSHIPv4Acl | Verifies if the SSHD agent has IPv4 ACL(s) configured. | - | failure | VRF: default - SSH IPv4 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifySSHIPv6Acl | Verifies if the SSHD agent has IPv6 ACL(s) configured. | - | failure | VRF: default - SSH IPv6 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifySSHStatus | Verifies if the SSHD agent is disabled in the default VRF. | - | failure | SSHD status for Default VRF is enabled |
| s1-spine1 | Security | VerifySpecificIPSecConn | Verifies the IPv4 security connections. | - | failure | Peer: 10.255.0.1 VRF: default - Not configured<br>Peer: 10.255.0.2 VRF: default - Not configured |
| s1-spine1 | Services | VerifyDNSServers | Verifies if the DNS (Domain Name Service) servers are correctly configured. | - | failure | Server 10.14.0.1 VRF: default Priority: 1 - Not configured<br>Server 10.14.0.11 VRF: MGMT Priority: 0 - Not configured |
| s1-spine1 | Services | VerifyErrdisableRecovery | Verifies the error disable recovery functionality. | - | failure | Reason: acl Status: Enabled Interval: 30 - Incorrect configuration - Status: Disabled Interval: 300<br>Reason: bpduguard Status: Enabled Interval: 30 - Incorrect configuration - Status: Disabled Interval: 300 |
| s1-spine1 | Services | VerifyHostname | Verifies the hostname of a device. | - | failure | Incorrect Hostname - Expected: s1-spine1 Actual: leaf1-dc1 |
| s1-spine1 | SNMP | VerifySnmpContact | Verifies the SNMP contact of a device. | - | failure | SNMP contact is not configured |
| s1-spine1 | SNMP | VerifySnmpErrorCounters | Verifies the SNMP error counters. | - | failure | SNMP counters not found |
| s1-spine1 | SNMP | VerifySnmpGroup | Verifies the SNMP group configurations for specified version(s). | - | failure | Group: Group1 Version: v1 - Not configured<br>Group: Group2 Version: v3 - Not configured |
| s1-spine1 | SNMP | VerifySnmpHostLogging | Verifies SNMP logging configurations. | - | failure | SNMP logging is disabled |
| s1-spine1 | SNMP | VerifySnmpIPv4Acl | Verifies if the SNMP agent has IPv4 ACL(s) configured. | - | failure | VRF: default - Incorrect SNMP IPv4 ACL(s) - Expected: 3 Actual: 0 |
| s1-spine1 | SNMP | VerifySnmpIPv6Acl | Verifies if the SNMP agent has IPv6 ACL(s) configured. | - | failure | VRF: default - Incorrect SNMP IPv6 ACL(s) - Expected: 3 Actual: 0 |
| s1-spine1 | SNMP | VerifySnmpLocation | Verifies the SNMP location of a device. | - | failure | SNMP location is not configured |
| s1-spine1 | SNMP | VerifySnmpNotificationHost | Verifies the SNMP notification host(s) (SNMP manager) configurations. | - | failure | No SNMP host is configured |
| s1-spine1 | SNMP | VerifySnmpPDUCounters | Verifies the SNMP PDU counters. | - | failure | SNMP counters not found |
| s1-spine1 | SNMP | VerifySnmpSourceInterface | Verifies SNMP source interfaces. | - | failure | SNMP source interface(s) not configured |
| s1-spine1 | SNMP | VerifySnmpStatus | Verifies if the SNMP agent is enabled. | - | failure | VRF: default - SNMP agent disabled |
| s1-spine1 | SNMP | VerifySnmpUser | Verifies the SNMP user configurations. | - | failure | User: test Group: test_group Version: v3 - Not found |
| s1-spine1 | Software | VerifyEOSVersion | Verifies the EOS version of the device. | - | failure | EOS version mismatch - Actual: 4.31.0F-33804048.4310F (engineering build) not in Expected: 4.25.4M, 4.26.1F |
| s1-spine1 | Software | VerifyTerminAttrVersion | Verifies the TerminAttr version of the device. | - | failure | TerminAttr version mismatch - Actual: v1.29.0 not in Expected: v1.13.6, v1.8.0 |
| s1-spine1 | STP | VerifySTPDisabledVlans | Verifies the STP disabled VLAN(s). | - | failure | VLAN: 6 - Not configured |
| s1-spine1 | STP | VerifySTPMode | Verifies the configured STP mode for a provided list of VLAN(s). | - | failure | VLAN 10 - Incorrect STP mode - Expected: rapidPvst Actual: mstp<br>VLAN 20 - Incorrect STP mode - Expected: rapidPvst Actual: mstp |
| s1-spine1 | STP | VerifySTPRootPriority | Verifies the STP root priority for a provided list of VLAN or MST instance ID(s). | - | failure | Instance: MST10 - Not configured<br>Instance: MST20 - Not configured |
| s1-spine1 | STUN | VerifyStunClient | (Deprecated) Verifies the translation for a source address on a STUN client. | - | failure | Client 172.18.3.2 Port: 4500 - STUN client translation not found |
| s1-spine1 | STUN | VerifyStunClientTranslation | Verifies the translation for a source address on a STUN client. | - | failure | Client 172.18.3.2 Port: 4500 - STUN client translation not found<br>Client 100.64.3.2 Port: 4500 - STUN client translation not found |
| s1-spine1 | STUN | VerifyStunServer | Verifies the STUN server status is enabled and running. | - | failure | STUN server status is disabled and not running |
| s1-spine1 | VLAN | VerifyDynamicVlanSource | Verifies dynamic VLAN allocation for specified VLAN sources. | - | failure | Dynamic VLAN source(s) exist but have no VLANs allocated: mlagsync |
| s1-spine1 | VLAN | VerifyVlanInternalPolicy | Verifies the VLAN internal allocation policy and the range of VLANs. | - | failure | VLAN internal allocation policy: ascending - Incorrect end VLAN id configured - Expected: 4094 Actual: 1199 |
| s1-spine1 | VLAN | VerifyVlanStatus | Verifies the administrative status of specified VLANs. | - | failure | VLAN: Vlan10 - Incorrect administrative status - Expected: suspended Actual: active |
| s1-spine1 | VXLAN | VerifyVxlanVniBinding | Verifies the VNI-VLAN, VNI-VRF bindings of the Vxlan1 interface. | - | failure | Interface: Vxlan1 VNI: 500 - Binding not found |
| s1-spine1 | VXLAN | VerifyVxlanVtep | Verifies Vxlan1 VTEP peers. | - | failure | The following VTEP peer(s) are missing from the Vxlan1 interface: 10.1.1.5, 10.1.1.6<br>Unexpected VTEP peer(s) on Vxlan1 interface: 10.100.2.3 |
## Test Results
| Device Under Test | Categories | Test | Description | Custom Field | Result | Messages |
| ----------------- | ---------- | ---- | ----------- | ------------ | ------ | -------- |
| s1-spine1 | AAA | VerifyAcctConsoleMethods | Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x). | - | failure | AAA console accounting is not configured for commands, exec, system, dot1x |
| s1-spine1 | AAA | VerifyAcctDefaultMethods | Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x). | - | failure | AAA default accounting is not configured for commands, exec, system, dot1x |
| s1-spine1 | AAA | VerifyAuthenMethods | Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x). | - | failure | AAA authentication methods are not configured for login console |
| s1-spine1 | AAA | VerifyAuthzMethods | Verifies the AAA authorization method lists for different authorization types (commands, exec). | - | failure | AAA authorization methods local, none, logging are not matching for commands, exec |
| s1-spine1 | AAA | VerifyTacacsServerGroups | Verifies if the provided TACACS server group(s) are configured. | - | failure | No TACACS server group(s) are configured |
| s1-spine1 | AAA | VerifyTacacsServers | Verifies TACACS servers are configured for a specified VRF. | - | failure | No TACACS servers are configured |
| s1-spine1 | AAA | VerifyTacacsSourceIntf | Verifies TACACS source-interface for a specified VRF. | - | failure | VRF: MGMT Source Interface: Management0 - Not configured |
| s1-spine1 | AVT | VerifyAVTPathHealth | Verifies the status of all AVT paths for all VRFs. | - | skipped | VerifyAVTPathHealth test is not supported on cEOSLab |
| s1-spine1 | AVT | VerifyAVTRole | Verifies the AVT role of a device. | - | skipped | VerifyAVTRole test is not supported on cEOSLab |
| s1-spine1 | AVT | VerifyAVTSpecificPath | Verifies the Adaptive Virtual Topology (AVT) path. | - | skipped | VerifyAVTSpecificPath test is not supported on cEOSLab |
| s1-spine1 | BFD | VerifyBFDPeersHealth | Verifies the health of IPv4 BFD peers across all VRFs. | - | success | - |
| s1-spine1 | BFD | VerifyBFDPeersIntervals | Verifies the timers of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.8 VRF: default - Not found<br>Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BFD | VerifyBFDPeersRegProtocols | Verifies the registered routing protocol of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BFD | VerifyBFDSpecificPeers | Verifies the state of IPv4 BFD peer sessions. | - | failure | Peer: 192.0.255.8 VRF: default - Not found<br>Peer: 192.0.255.7 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPAdvCommunities | Verifies the advertised communities of BGP peers. | - | failure | Peer: 172.30.11.17 VRF: default - Not found<br>Peer: 172.30.11.21 VRF: MGMT - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPExchangedRoutes | Verifies the advertised and received routes of BGP IPv4 peer(s). | - | failure | Peer: 172.30.255.5 VRF: default Advertised route: 192.0.254.5/32 - Not found<br>Peer: 172.30.255.5 VRF: default Received route: 192.0.255.4/32 - Not found<br>Peer: 172.30.255.1 VRF: default Advertised route: 192.0.255.1/32 - Not found<br>Peer: 172.30.255.1 VRF: default Advertised route: 192.0.254.5/32 - Not found |
| s1-spine1 | BGP | VerifyBGPNlriAcceptance | Verifies that all received NLRI are accepted for all AFI/SAFI configured for BGP peers. | - | failure | Peer: 10.100.0.128 VRF: default - Not found<br>Peer: 2001:db8:1::2 VRF: default - Not found<br>Peer: fe80::2%Et1 VRF: default - Not found<br>Peer: fe80::2%Et1 VRF: default - Not found |
| s1-spine1 | BGP | VerifyBGPPeerASNCap | Verifies the four octet ASN capability of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerCount | Verifies the count of BGP peers for given address families. | - | failure | AFI: ipv4 SAFI: unicast VRF: PROD - VRF not configured<br>AFI: ipv4 SAFI: multicast VRF: DEV - VRF not configured |
| s1-spine1 | BGP | VerifyBGPPeerDropStats | Verifies BGP NLRI drop statistics of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerGroup | Verifies BGP peer group of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerMD5Auth | Verifies the MD5 authentication and state of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.5 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: default - Session does not have MD5 authentication enabled |
| s1-spine1 | BGP | VerifyBGPPeerMPCaps | Verifies the multiprotocol capabilities of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: default - ipv4MplsLabels not found<br>Interface: Ethernet1 VRF: default - ipv4MplsVpn not found |
| s1-spine1 | BGP | VerifyBGPPeerRouteLimit | Verifies maximum routes and warning limit for BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerRouteRefreshCap | Verifies the route refresh capabilities of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerSession | Verifies the session state of BGP peers. | - | failure | Peer: 10.1.0.1 VRF: default - Not found<br>Peer: 10.1.0.2 VRF: default - Not found<br>Peer: 10.1.255.2 VRF: DEV - Not found<br>Peer: 10.1.255.4 VRF: DEV - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Vlan3499 VRF: PROD - Not found |
| s1-spine1 | BGP | VerifyBGPPeerSessionRibd | Verifies the session state of BGP peers. | - | failure | Peer: 10.1.0.1 VRF: default - Not found<br>Peer: 10.1.255.4 VRF: DEV - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerTtlMultiHops | Verifies BGP TTL and max-ttl-hops count for BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.2 VRF: test - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeerUpdateErrors | Verifies BGP update error counters of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBGPPeersHealth | Verifies the health of BGP peers for given address families. | - | failure | AFI: ipv6 SAFI: unicast VRF: DEV - VRF not configured |
| s1-spine1 | BGP | VerifyBGPPeersHealthRibd | Verifies the health of all the BGP peers. | - | success | - |
| s1-spine1 | BGP | VerifyBGPRedistribution | Verifies BGP redistribution. | - | error | show bgp instance vrf all has failed: Invalid requested version for ModelMetaClass: no such revision 4, most current is 3 |
| s1-spine1 | BGP | VerifyBGPRouteECMP | Verifies BGP IPv4 route ECMP paths. | - | success | - |
| s1-spine1 | BGP | VerifyBGPRoutePaths | Verifies BGP IPv4 route paths. | - | success | - |
| s1-spine1 | BGP | VerifyBGPSpecificPeers | Verifies the health of specific BGP peer(s) for given address families. | - | failure | AFI: evpn Peer: 10.1.0.1 - Not configured<br>AFI: evpn Peer: 10.1.0.2 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.254.1 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.0 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.2 - Not configured<br>AFI: ipv4 SAFI: unicast VRF: default Peer: 10.1.255.4 - Not configured |
| s1-spine1 | BGP | VerifyBGPTimers | Verifies the timers of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: 172.30.11.5 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyBgpRouteMaps | Verifies BGP inbound and outbound route-maps of BGP peers. | - | failure | Peer: 172.30.11.1 VRF: default - Not found<br>Peer: fd00:dc:1::1 VRF: default - Not found<br>Interface: Ethernet1 VRF: MGMT - Not found |
| s1-spine1 | BGP | VerifyEVPNType2Route | Verifies the EVPN Type-2 routes for a given IPv4 or MAC address and VNI. | - | failure | Address: 192.168.20.102 VNI: 10020 - No EVPN Type-2 route<br>Address: aa:c1:ab:5d:b4:1e VNI: 10010 - No EVPN Type-2 route |
| s1-spine1 | BGP | VerifyEVPNType5Routes | Verifies EVPN Type-5 routes for given IP prefixes and VNIs. | - | failure | Prefix: 192.168.10.0/24 VNI: 10 - No EVPN Type-5 routes found<br>Prefix: 192.168.20.0/24 VNI: 20 - No EVPN Type-5 routes found<br>Prefix: 192.168.30.0/24 VNI: 30 - No EVPN Type-5 routes found<br>Prefix: 192.168.40.0/24 VNI: 40 - No EVPN Type-5 routes found |
| s1-spine1 | Configuration | VerifyRunningConfigDiffs | Verifies there is no difference between the running-config and the startup-config. | - | failure | --- flash:/startup-config<br>+++ system:/running-config<br>@@ -18,6 +18,7 @@<br> hostname leaf1-dc1<br> ip name-server vrf MGMT 10.14.0.1<br> dns domain fun.aristanetworks.com<br>+ip host vitthal 192.168.66.220<br> !<br> platform tfa<br> personality arfa<br> |
| s1-spine1 | Configuration | VerifyRunningConfigLines | Search the Running-Config for the given RegEx patterns. | - | failure | Following patterns were not found: '^enable password.*$', 'bla bla' |
| s1-spine1 | Configuration | VerifyZeroTouch | Verifies ZeroTouch is disabled. | - | success | - |
| s1-spine1 | Connectivity | VerifyLLDPNeighbors | Verifies the connection status of the specified LLDP (Link Layer Discovery Protocol) neighbors. | - | failure | Port: Ethernet1 Neighbor: DC1-SPINE1 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: spine1-dc1.fun.aristanetworks.com/Ethernet3<br>Port: Ethernet2 Neighbor: DC1-SPINE2 Neighbor Port: Ethernet1 - Wrong LLDP neighbors: spine2-dc1.fun.aristanetworks.com/Ethernet3 |
| s1-spine1 | Connectivity | VerifyReachability | Test network reachability to one or many destination IP(s). | - | success | - |
| s1-spine1 | Cvx | VerifyActiveCVXConnections | Verifies the number of active CVX Connections. | - | failure | 'show cvx connections brief' failed on s1-spine1: Unavailable command (controller not ready) (at token 2: 'connections') |
| s1-spine1 | Cvx | VerifyCVXClusterStatus | Verifies the CVX Server Cluster status. | - | failure | CVX Server status is not enabled<br>CVX Server is not a cluster |
| s1-spine1 | Cvx | VerifyManagementCVX | Verifies the management CVX global status. | - | failure | Management CVX status is not valid: Expected: enabled Actual: disabled |
| s1-spine1 | Cvx | VerifyMcsClientMounts | Verify if all MCS client mounts are in mountStateMountComplete. | - | failure | MCS Client mount states are not present |
| s1-spine1 | Cvx | VerifyMcsServerMounts | Verify if all MCS server mounts are in a MountComplete state. | - | failure | 'show cvx mounts' failed on s1-spine1: Unavailable command (controller not ready) (at token 2: 'mounts') |
| s1-spine1 | Field Notices | VerifyFieldNotice44Resolution | Verifies that the device is using the correct Aboot version per FN0044. | - | skipped | VerifyFieldNotice44Resolution test is not supported on cEOSLab |
| s1-spine1 | Field Notices | VerifyFieldNotice72Resolution | Verifies if the device is exposed to FN0072, and if the issue has been mitigated. | - | skipped | VerifyFieldNotice72Resolution test is not supported on cEOSLab |
| s1-spine1 | Flow Tracking | VerifyHardwareFlowTrackerStatus | Verifies the hardware flow tracking state. | - | skipped | VerifyHardwareFlowTrackerStatus test is not supported on cEOSLab |
| s1-spine1 | Greent | VerifyGreenT | Verifies if a GreenT policy other than the default is created. | - | failure | No GreenT policy is created |
| s1-spine1 | Greent | VerifyGreenTCounters | Verifies if the GreenT counters are incremented. | - | failure | GreenT counters are not incremented |
| s1-spine1 | Hardware | VerifyAdverseDrops | Verifies there are no adverse drops on DCS-7280 and DCS-7500 family switches. | - | skipped | VerifyAdverseDrops test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyEnvironmentCooling | Verifies the status of power supply fans and all fan trays. | - | skipped | VerifyEnvironmentCooling test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyEnvironmentPower | Verifies the power supplies status. | - | skipped | VerifyEnvironmentPower test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyEnvironmentSystemCooling | Verifies the device's system cooling status. | - | skipped | VerifyEnvironmentSystemCooling test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyTemperature | Verifies if the device temperature is within acceptable limits. | - | skipped | VerifyTemperature test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyTransceiversManufacturers | Verifies if all the transceivers come from approved manufacturers. | - | skipped | VerifyTransceiversManufacturers test is not supported on cEOSLab |
| s1-spine1 | Hardware | VerifyTransceiversTemperature | Verifies if all the transceivers are operating at an acceptable temperature. | - | skipped | VerifyTransceiversTemperature test is not supported on cEOSLab |
| s1-spine1 | Interfaces | VerifyIPProxyARP | Verifies if Proxy ARP is enabled. | - | failure | Interface: Ethernet1 - Proxy-ARP disabled<br>Interface: Ethernet2 - Proxy-ARP disabled |
| s1-spine1 | Interfaces | VerifyIllegalLACP | Verifies there are no illegal LACP packets in all port channels. | - | success | - |
| s1-spine1 | Interfaces | VerifyInterfaceErrDisabled | Verifies there are no interfaces in the errdisabled state. | - | success | - |
| s1-spine1 | Interfaces | VerifyInterfaceErrors | Verifies that the interfaces error counters are equal to zero. | - | success | - |
| s1-spine1 | Interfaces | VerifyInterfaceIPv4 | Verifies the interface IPv4 addresses. | - | failure | Interface: Ethernet2 - IP address mismatch - Expected: 172.30.11.1/31 Actual: 10.100.0.11/31<br>Interface: Ethernet2 - Secondary IP address is not configured |
| s1-spine1 | Interfaces | VerifyInterfaceUtilization | Verifies that the utilization of interfaces is below a certain threshold. | - | success | - |
| s1-spine1 | Interfaces | VerifyInterfacesSpeed | Verifies the speed, lanes, auto-negotiation status, and mode as full duplex for interfaces. | - | failure | Interface: Ethernet2 - Bandwidth mismatch - Expected: 10.0Gbps Actual: 1Gbps<br>Interface: Ethernet3 - Bandwidth mismatch - Expected: 100.0Gbps Actual: 1Gbps<br>Interface: Ethernet3 - Auto-negotiation mismatch - Expected: success Actual: unknown<br>Interface: Ethernet3 - Data lanes count mismatch - Expected: 1 Actual: 0<br>Interface: Ethernet2 - Bandwidth mismatch - Expected: 2.5Gbps Actual: 1Gbps |
| s1-spine1 | Interfaces | VerifyInterfacesStatus | Verifies the operational states of specified interfaces to ensure they match expected configurations. | interface | failure | Port-Channel100 - Not configured<br>Ethernet49/1 - Not configured |
| s1-spine1 | Interfaces | VerifyIpVirtualRouterMac | Verifies the IP virtual router MAC address. | - | success | - |
| s1-spine1 | Interfaces | VerifyL2MTU | Verifies the global L2 MTU of all L2 interfaces. | - | failure | Interface: Ethernet3 - Incorrect MTU configured - Expected: 1500 Actual: 9214<br>Interface: Port-Channel5 - Incorrect MTU configured - Expected: 1500 Actual: 9214 |
| s1-spine1 | Interfaces | VerifyL3MTU | Verifies the global L3 MTU of all L3 interfaces. | - | failure | Interface: Ethernet2 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Ethernet1 - Incorrect MTU - Expected: 2500 Actual: 9214<br>Interface: Loopback0 - Incorrect MTU - Expected: 1500 Actual: 65535<br>Interface: Loopback1 - Incorrect MTU - Expected: 1500 Actual: 65535<br>Interface: Vlan1006 - Incorrect MTU - Expected: 1500 Actual: 9164<br>Interface: Vlan1199 - Incorrect MTU - Expected: 1500 Actual: 9164<br>Interface: Vlan4093 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan4094 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan3019 - Incorrect MTU - Expected: 1500 Actual: 9214<br>Interface: Vlan3009 - Incorrect MTU - Expected: 1500 Actual: 9214 |
| s1-spine1 | Interfaces | VerifyLACPInterfacesStatus | Verifies the Link Aggregation Control Protocol (LACP) status of the interface. | - | failure | Interface: Ethernet1 Port-Channel: Port-Channel100 - Not configured |
| s1-spine1 | Interfaces | VerifyLoopbackCount | Verifies the number of loopback interfaces and their status. | - | failure | Loopback interface(s) count mismatch: Expected 3 Actual: 2 |
| s1-spine1 | Interfaces | VerifyPortChannels | Verifies there are no inactive ports in all port channels. | - | success | - |
| s1-spine1 | Interfaces | VerifySVI | Verifies the status of all SVIs. | - | success | - |
| s1-spine1 | Interfaces | VerifyStormControlDrops | Verifies there are no interface storm-control drop counters. | - | skipped | VerifyStormControlDrops test is not supported on cEOSLab |
| s1-spine1 | ISIS | VerifyISISGracefulRestart | Verifies the IS-IS graceful restart feature. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS | VerifyISISInterfaceMode | Verifies IS-IS interfaces are running in the correct mode. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS | VerifyISISNeighborCount | Verifies the number of IS-IS neighbors per interface and level. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS | VerifyISISNeighborState | Verifies the health of IS-IS neighbors. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS, Segment-Routing | VerifyISISSegmentRoutingAdjacencySegments | Verifies IS-IS segment routing adjacency segments. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS, Segment-Routing | VerifyISISSegmentRoutingDataplane | Verifies IS-IS segment routing data-plane configuration. | - | skipped | IS-IS not configured |
| s1-spine1 | ISIS, Segment-Routing | VerifyISISSegmentRoutingTunnels | Verify ISIS-SR tunnels computed by device. | - | skipped | IS-IS-SR not configured |
| s1-spine1 | LANZ | VerifyLANZ | Verifies if LANZ is enabled. | - | skipped | VerifyLANZ test is not supported on cEOSLab |
| s1-spine1 | Logging | VerifyLoggingAccounting | Verifies if AAA accounting logs are generated. | - | success | - |
| s1-spine1 | Logging | VerifyLoggingEntries | Verifies that the expected log string is present in the last specified log messages. | - | failure | Pattern: `.*ACCOUNTING-5-EXEC: cvpadmin ssh.*` - Not found in last 30 alerts log entries<br>Pattern: `.*SPANTREE-6-INTERFACE_ADD:.*` - Not found in last 10 critical log entries |
| s1-spine1 | Logging | VerifyLoggingErrors | Verifies there are no syslog messages with a severity of ERRORS or higher. | - | failure | Device has reported syslog messages with a severity of ERRORS or higher:<br>Apr 29 08:01:27 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: sent to neighbor 10.100.4.5 (VRF data AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> Apr 29 08:01:27 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: sent to neighbor 10.100.4.5 (VRF guest AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> Apr 29 08:01:29 leaf1-dc1 Bgp: %BGP-3-NOTIFICATION: received from neighbor 10.100.4.5 (VRF guest AS 65102) 6/7 (Cease/connection collision resolution) 0 bytes <br> <br> |
| s1-spine1 | Logging | VerifyLoggingHostname | Verifies if logs are generated with the device FQDN. | - | failure | Logs are not generated with the device FQDN |
| s1-spine1 | Logging | VerifyLoggingHosts | Verifies logging hosts (syslog servers) for a specified VRF. | - | failure | Syslog servers 1.1.1.1, 2.2.2.2 are not configured in VRF default |
| s1-spine1 | Logging | VerifyLoggingLogsGeneration | Verifies if logs are generated. | - | success | - |
| s1-spine1 | Logging | VerifyLoggingPersistent | Verifies if logging persistent is enabled and logs are saved in flash. | - | failure | Persistent logging is disabled |
| s1-spine1 | Logging | VerifyLoggingSourceIntf | Verifies logging source-interface for a specified VRF. | - | failure | Source-interface: Management0 VRF: default - Not configured |
| s1-spine1 | Logging | VerifyLoggingTimestamp | Verifies if logs are generated with the appropriate timestamp. | - | failure | Logs are not generated with the appropriate timestamp format |
| s1-spine1 | Logging | VerifySyslogLogging | Verifies if syslog logging is enabled. | - | success | - |
| s1-spine1 | MLAG | VerifyMlagConfigSanity | Verifies there are no MLAG config-sanity inconsistencies. | - | success | - |
| s1-spine1 | MLAG | VerifyMlagDualPrimary | Verifies the MLAG dual-primary detection parameters. | - | failure | Dual-primary detection is disabled |
| s1-spine1 | MLAG | VerifyMlagInterfaces | Verifies there are no inactive or active-partial MLAG ports. | - | success | - |
| s1-spine1 | MLAG | VerifyMlagPrimaryPriority | Verifies the configuration of the MLAG primary priority. | - | failure | MLAG primary priority mismatch - Expected: 3276 Actual: 32767 |
| s1-spine1 | MLAG | VerifyMlagReloadDelay | Verifies the reload-delay parameters of the MLAG configuration. | - | success | - |
| s1-spine1 | MLAG | VerifyMlagStatus | Verifies the health status of the MLAG configuration. | - | success | - |
| s1-spine1 | Multicast | VerifyIGMPSnoopingGlobal | Verifies the IGMP snooping global status. | - | success | - |
| s1-spine1 | Multicast | VerifyIGMPSnoopingVlans | Verifies the IGMP snooping status for the provided VLANs. | - | failure | VLAN10 - Incorrect IGMP state - Expected: disabled Actual: enabled<br>Supplied vlan 12 is not present on the device |
| s1-spine1 | OSPF | VerifyOSPFMaxLSA | Verifies all OSPF instances did not cross the maximum LSA threshold. | - | skipped | OSPF not configured |
| s1-spine1 | OSPF | VerifyOSPFNeighborCount | Verifies the number of OSPF neighbors in FULL state is the one we expect. | - | skipped | OSPF not configured |
| s1-spine1 | OSPF | VerifyOSPFNeighborState | Verifies all OSPF neighbors are in FULL state. | - | skipped | OSPF not configured |
| s1-spine1 | Path-Selection | VerifyPathsHealth | Verifies the path and telemetry state of all paths under router path-selection. | - | skipped | VerifyPathsHealth test is not supported on cEOSLab |
| s1-spine1 | Path-Selection | VerifySpecificPath | Verifies the DPS path and telemetry state of an IPv4 peer. | - | skipped | VerifySpecificPath test is not supported on cEOSLab |
| s1-spine1 | Profiles | VerifyTcamProfile | Verifies the device TCAM profile. | - | skipped | VerifyTcamProfile test is not supported on cEOSLab |
| s1-spine1 | Profiles | VerifyUnifiedForwardingTableMode | Verifies the device is using the expected UFT mode. | - | skipped | VerifyUnifiedForwardingTableMode test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpGMStatus | Verifies that the device is locked to a valid PTP Grandmaster. | - | skipped | VerifyPtpGMStatus test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpLockStatus | Verifies that the device was locked to the upstream PTP GM in the last minute. | - | skipped | VerifyPtpLockStatus test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpModeStatus | Verifies that the device is configured as a PTP Boundary Clock. | - | skipped | VerifyPtpModeStatus test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpOffset | Verifies that the PTP timing offset is within +/- 1000ns from the master clock. | - | skipped | VerifyPtpOffset test is not supported on cEOSLab |
| s1-spine1 | PTP | VerifyPtpPortModeStatus | Verifies the PTP interfaces state. | - | skipped | VerifyPtpPortModeStatus test is not supported on cEOSLab |
| s1-spine1 | Routing | VerifyIPv4RouteNextHops | Verifies the next-hops of the IPv4 prefixes. | - | success | - |
| s1-spine1 | Routing | VerifyIPv4RouteType | Verifies the route-type of the IPv4 prefixes. | - | failure | Prefix: 10.100.0.12/31 VRF: default - Route not found<br>Prefix: 10.100.1.5/32 VRF: default - Incorrect route type - Expected: iBGP Actual: connected |
| s1-spine1 | Routing | VerifyRoutingProtocolModel | Verifies the configured routing protocol model. | - | success | - |
| s1-spine1 | Routing | VerifyRoutingStatus | Verifies the routing status for IPv4/IPv6 unicast, multicast, and IPv6 interfaces (RFC5549). | - | failure | IPv6 unicast routing enabled status mismatch - Expected: True Actual: False |
| s1-spine1 | Routing | VerifyRoutingTableEntry | Verifies that the provided routes are present in the routing table of a specified VRF. | - | failure | The following route(s) are missing from the routing table of VRF default: 10.1.0.1, 10.1.0.2 |
| s1-spine1 | Routing | VerifyRoutingTableSize | Verifies the size of the IP routing table of the default VRF. | - | failure | Routing table routes are outside the routes range - Expected: 2 <= to >= 20 Actual: 35 |
| s1-spine1 | Security | VerifyAPIHttpStatus | Verifies if eAPI HTTP server is disabled globally. | - | success | - |
| s1-spine1 | Security | VerifyAPIHttpsSSL | Verifies if the eAPI has a valid SSL profile. | - | failure | eAPI HTTPS server SSL profile default is not configured |
| s1-spine1 | Security | VerifyAPIIPv4Acl | Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF. | - | failure | VRF: default - eAPI IPv4 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifyAPIIPv6Acl | Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF. | - | failure | VRF: default - eAPI IPv6 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifyAPISSLCertificate | Verifies the eAPI SSL certificate expiry, common subject name, encryption algorithm and key size. | - | success | - |
| s1-spine1 | Security | VerifyBannerLogin | Verifies the login banner of a device. | - | failure | Login banner is not configured |
| s1-spine1 | Security | VerifyBannerMotd | Verifies the motd banner of a device. | - | failure | MOTD banner is not configured |
| s1-spine1 | Security | VerifyHardwareEntropy | Verifies hardware entropy generation is enabled on device. | - | failure | Hardware entropy generation is disabled |
| s1-spine1 | Security | VerifyIPSecConnHealth | Verifies all IPv4 security connections. | - | failure | No IPv4 security connection configured |
| s1-spine1 | Security | VerifyIPv4ACL | Verifies the configuration of IPv4 ACLs. | - | failure | ACL name: LabTest - Not configured |
| s1-spine1 | Security | VerifySSHIPv4Acl | Verifies if the SSHD agent has IPv4 ACL(s) configured. | - | failure | VRF: default - SSH IPv4 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifySSHIPv6Acl | Verifies if the SSHD agent has IPv6 ACL(s) configured. | - | failure | VRF: default - SSH IPv6 ACL(s) count mismatch - Expected: 3 Actual: 0 |
| s1-spine1 | Security | VerifySSHStatus | Verifies if the SSHD agent is disabled in the default VRF. | - | failure | SSHD status for Default VRF is enabled |
| s1-spine1 | Security | VerifySpecificIPSecConn | Verifies the IPv4 security connections. | - | failure | Peer: 10.255.0.1 VRF: default - Not configured<br>Peer: 10.255.0.2 VRF: default - Not configured |
| s1-spine1 | Security | VerifyTelnetStatus | Verifies if Telnet is disabled in the default VRF. | - | success | - |
| s1-spine1 | Services | VerifyDNSLookup | Verifies the DNS name to IP address resolution. | - | success | - |
| s1-spine1 | Services | VerifyDNSServers | Verifies if the DNS (Domain Name Service) servers are correctly configured. | - | failure | Server 10.14.0.1 VRF: default Priority: 1 - Not configured<br>Server 10.14.0.11 VRF: MGMT Priority: 0 - Not configured |
| s1-spine1 | Services | VerifyErrdisableRecovery | Verifies the error disable recovery functionality. | - | failure | Reason: acl Status: Enabled Interval: 30 - Incorrect configuration - Status: Disabled Interval: 300<br>Reason: bpduguard Status: Enabled Interval: 30 - Incorrect configuration - Status: Disabled Interval: 300 |
| s1-spine1 | Services | VerifyHostname | Verifies the hostname of a device. | - | failure | Incorrect Hostname - Expected: s1-spine1 Actual: leaf1-dc1 |
| s1-spine1 | SNMP | VerifySnmpContact | Verifies the SNMP contact of a device. | - | failure | SNMP contact is not configured |
| s1-spine1 | SNMP | VerifySnmpErrorCounters | Verifies the SNMP error counters. | - | failure | SNMP counters not found |
| s1-spine1 | SNMP | VerifySnmpGroup | Verifies the SNMP group configurations for specified version(s). | - | failure | Group: Group1 Version: v1 - Not configured<br>Group: Group2 Version: v3 - Not configured |
| s1-spine1 | SNMP | VerifySnmpHostLogging | Verifies SNMP logging configurations. | - | failure | SNMP logging is disabled |
| s1-spine1 | SNMP | VerifySnmpIPv4Acl | Verifies if the SNMP agent has IPv4 ACL(s) configured. | - | failure | VRF: default - Incorrect SNMP IPv4 ACL(s) - Expected: 3 Actual: 0 |
| s1-spine1 | SNMP | VerifySnmpIPv6Acl | Verifies if the SNMP agent has IPv6 ACL(s) configured. | - | failure | VRF: default - Incorrect SNMP IPv6 ACL(s) - Expected: 3 Actual: 0 |
| s1-spine1 | SNMP | VerifySnmpLocation | Verifies the SNMP location of a device. | - | failure | SNMP location is not configured |
| s1-spine1 | SNMP | VerifySnmpNotificationHost | Verifies the SNMP notification host(s) (SNMP manager) configurations. | - | failure | No SNMP host is configured |
| s1-spine1 | SNMP | VerifySnmpPDUCounters | Verifies the SNMP PDU counters. | - | failure | SNMP counters not found |
| s1-spine1 | SNMP | VerifySnmpSourceInterface | Verifies SNMP source interfaces. | - | failure | SNMP source interface(s) not configured |
| s1-spine1 | SNMP | VerifySnmpStatus | Verifies if the SNMP agent is enabled. | - | failure | VRF: default - SNMP agent disabled |
| s1-spine1 | SNMP | VerifySnmpUser | Verifies the SNMP user configurations. | - | failure | User: test Group: test_group Version: v3 - Not found |
| s1-spine1 | Software | VerifyEOSExtensions | Verifies that all EOS extensions installed on the device are enabled for boot persistence. | - | success | - |
| s1-spine1 | Software | VerifyEOSVersion | Verifies the EOS version of the device. | - | failure | EOS version mismatch - Actual: 4.31.0F-33804048.4310F (engineering build) not in Expected: 4.25.4M, 4.26.1F |
| s1-spine1 | Software | VerifyTerminAttrVersion | Verifies the TerminAttr version of the device. | - | failure | TerminAttr version mismatch - Actual: v1.29.0 not in Expected: v1.13.6, v1.8.0 |
| s1-spine1 | STP | VerifySTPBlockedPorts | Verifies there is no STP blocked ports. | - | success | - |
| s1-spine1 | STP | VerifySTPCounters | Verifies there is no errors in STP BPDU packets. | - | success | - |
| s1-spine1 | STP | VerifySTPDisabledVlans | Verifies the STP disabled VLAN(s). | - | failure | VLAN: 6 - Not configured |
| s1-spine1 | STP | VerifySTPForwardingPorts | Verifies that all interfaces are forwarding for a provided list of VLAN(s). | - | success | - |
| s1-spine1 | STP | VerifySTPMode | Verifies the configured STP mode for a provided list of VLAN(s). | - | failure | VLAN 10 - Incorrect STP mode - Expected: rapidPvst Actual: mstp<br>VLAN 20 - Incorrect STP mode - Expected: rapidPvst Actual: mstp |
| s1-spine1 | STP | VerifySTPRootPriority | Verifies the STP root priority for a provided list of VLAN or MST instance ID(s). | - | failure | Instance: MST10 - Not configured<br>Instance: MST20 - Not configured |
| s1-spine1 | STP | VerifyStpTopologyChanges | Verifies the number of changes across all interfaces in the Spanning Tree Protocol (STP) topology is below a threshold. | - | success | - |
| s1-spine1 | STUN | VerifyStunClient | (Deprecated) Verifies the translation for a source address on a STUN client. | - | failure | Client 172.18.3.2 Port: 4500 - STUN client translation not found |
| s1-spine1 | STUN | VerifyStunClientTranslation | Verifies the translation for a source address on a STUN client. | - | failure | Client 172.18.3.2 Port: 4500 - STUN client translation not found<br>Client 100.64.3.2 Port: 4500 - STUN client translation not found |
| s1-spine1 | STUN | VerifyStunServer | Verifies the STUN server status is enabled and running. | - | failure | STUN server status is disabled and not running |
| s1-spine1 | System | VerifyAgentLogs | Verifies there are no agent crash reports. | - | success | - |
| s1-spine1 | System | VerifyCPUUtilization | Verifies whether the CPU utilization is below 75%. | - | success | - |
| s1-spine1 | System | VerifyCoredump | Verifies there are no core dump files. | - | success | - |
| s1-spine1 | System | VerifyFileSystemUtilization | Verifies that no partition is utilizing more than 75% of its disk space. | - | success | - |
| s1-spine1 | System | VerifyMaintenance | Verifies that the device is not currently under or entering maintenance. | - | success | - |
| s1-spine1 | System | VerifyNTP | Verifies if NTP is synchronised. | - | success | - |
| s1-spine1 | System | VerifyReloadCause | Verifies the last reload cause of the device. | reload-cause | success | - |
| s1-spine1 | System | VerifyUptime | Verifies the device uptime. | - | success | - |
| s1-spine1 | VLAN | VerifyDynamicVlanSource | Verifies dynamic VLAN allocation for specified VLAN sources. | - | failure | Dynamic VLAN source(s) exist but have no VLANs allocated: mlagsync |
| s1-spine1 | VLAN | VerifyVlanInternalPolicy | Verifies the VLAN internal allocation policy and the range of VLANs. | - | failure | VLAN internal allocation policy: ascending - Incorrect end VLAN id configured - Expected: 4094 Actual: 1199 |
| s1-spine1 | VLAN | VerifyVlanStatus | Verifies the administrative status of specified VLANs. | - | failure | VLAN: Vlan10 - Incorrect administrative status - Expected: suspended Actual: active |
| s1-spine1 | VXLAN | VerifyVxlan1ConnSettings | Verifies Vxlan1 source interface and UDP port. | - | success | - |
| s1-spine1 | VXLAN | VerifyVxlan1Interface | Verifies the Vxlan1 interface status. | - | success | - |
| s1-spine1 | VXLAN | VerifyVxlanConfigSanity | Verifies there are no VXLAN config-sanity inconsistencies. | - | success | - |
| s1-spine1 | VXLAN | VerifyVxlanVniBinding | Verifies the VNI-VLAN, VNI-VRF bindings of the Vxlan1 interface. | - | failure | Interface: Vxlan1 VNI: 500 - Binding not found |
| s1-spine1 | VXLAN | VerifyVxlanVtep | Verifies Vxlan1 VTEP peers. | - | failure | The following VTEP peer(s) are missing from the Vxlan1 interface: 10.1.1.5, 10.1.1.6<br>Unexpected VTEP peer(s) on Vxlan1 interface: 10.100.2.3 |

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))