Adding upstream version 1.1.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 11:54:23 +01:00
parent f13b7abbd8
commit 77504588ab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
196 changed files with 10121 additions and 3780 deletions

View file

@ -1,4 +1,33 @@
# Copyright (c) 2023-2024 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 for anta.tests submodule."""
"""Tests for anta.tests module."""
import asyncio
from typing import Any
from anta.device import AntaDevice
def test(device: AntaDevice, data: dict[str, Any]) -> 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.
"""
# Instantiate the AntaTest subclass
test_instance = data["test"](device, inputs=data["inputs"], eos_data=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"]:
# We expect messages in test result
assert len(test_instance.result.messages) == len(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
assert expected in message
else:
# Test result should not have messages
assert test_instance.result.messages == []

View file

@ -0,0 +1,35 @@
# Copyright (c) 2023-2024 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# 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."""
from typing import Any
import pytest
def build_test_id(val: dict[str, Any]) -> str:
"""Build id for a unit test of an AntaTest subclass.
{
"name": "meaniful test name",
"test": <AntaTest instance>,
...
}
"""
return f"{val['test'].__module__}.{val['test'].__name__}-{val['name']}"
def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
"""Generate ANTA testts unit tests dynamically during test collection.
It will parametrize test cases based on the `DATA` data structure defined in `tests.units.anta_tests` modules.
See `tests/units/anta_tests/README.md` for more information on how to use it.
Test IDs are generated using the `build_test_id` function above.
Checking that only the function "test" is parametrized with data to allow for writing tests for helper functions
in each module.
"""
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)

File diff suppressed because it is too large Load diff

View file

@ -5,10 +5,14 @@
from __future__ import annotations
import sys
from typing import Any
import pytest
from pydantic import ValidationError
from anta.tests.routing.generic import VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -66,16 +70,6 @@ DATA: list[dict[str, Any]] = [
"inputs": {"minimum": 42, "maximum": 666},
"expected": {"result": "failure", "messages": ["routing-table has 1000 routes and not between min (42) and maximum (666)"]},
},
{
"name": "error-max-smaller-than-min",
"test": VerifyRoutingTableSize,
"eos_data": [{}],
"inputs": {"minimum": 666, "maximum": 42},
"expected": {
"result": "error",
"messages": ["Minimum 666 is greater than maximum 42"],
},
},
{
"name": "success",
"test": VerifyRoutingTableEntry,
@ -130,6 +124,48 @@ DATA: list[dict[str, Any]] = [
"inputs": {"vrf": "default", "routes": ["10.1.0.1", "10.1.0.2"]},
"expected": {"result": "success"},
},
{
"name": "success-collect-all",
"test": VerifyRoutingTableEntry,
"eos_data": [
{
"vrfs": {
"default": {
"routingDisabled": False,
"allRoutesProgrammedHardware": True,
"allRoutesProgrammedKernel": True,
"defaultRouteState": "notSet",
"routes": {
"10.1.0.1/32": {
"hardwareProgrammed": True,
"routeType": "eBGP",
"routeLeaked": False,
"kernelProgrammed": True,
"routeAction": "forward",
"directlyConnected": False,
"preference": 20,
"metric": 0,
"vias": [{"nexthopAddr": "10.1.255.4", "interface": "Ethernet1"}],
},
"10.1.0.2/32": {
"hardwareProgrammed": True,
"routeType": "eBGP",
"routeLeaked": False,
"kernelProgrammed": True,
"routeAction": "forward",
"directlyConnected": False,
"preference": 20,
"metric": 0,
"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"},
},
{
"name": "failure-missing-route",
"test": VerifyRoutingTableEntry,
@ -226,4 +262,75 @@ DATA: list[dict[str, Any]] = [
"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']"]},
},
{
"name": "failure-wrong-route-collect-all",
"test": VerifyRoutingTableEntry,
"eos_data": [
{
"vrfs": {
"default": {
"routingDisabled": False,
"allRoutesProgrammedHardware": True,
"allRoutesProgrammedKernel": True,
"defaultRouteState": "notSet",
"routes": {
"10.1.0.1/32": {
"hardwareProgrammed": True,
"routeType": "eBGP",
"routeLeaked": False,
"kernelProgrammed": True,
"routeAction": "forward",
"directlyConnected": False,
"preference": 20,
"metric": 0,
"vias": [{"nexthopAddr": "10.1.255.4", "interface": "Ethernet1"}],
},
"10.1.0.55/32": {
"hardwareProgrammed": True,
"routeType": "eBGP",
"routeLeaked": False,
"kernelProgrammed": True,
"routeAction": "forward",
"directlyConnected": False,
"preference": 20,
"metric": 0,
"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']"]},
},
]
class TestVerifyRoutingTableSizeInputs:
"""Test anta.tests.routing.generic.VerifyRoutingTableSize.Input."""
@pytest.mark.parametrize(
("minimum", "maximum"),
[
pytest.param(0, 0, id="zero"),
pytest.param(1, 2, id="1<2"),
pytest.param(0, sys.maxsize, id="max"),
],
)
def test_valid(self, minimum: int, maximum: int) -> None:
"""Test VerifyRoutingTableSize valid inputs."""
VerifyRoutingTableSize.Input(minimum=minimum, maximum=maximum)
@pytest.mark.parametrize(
("minimum", "maximum"),
[
pytest.param(-2, -1, id="negative"),
pytest.param(2, 1, id="2<1"),
pytest.param(sys.maxsize, 0, id="max"),
],
)
def test_invalid(self, minimum: int, maximum: int) -> None:
"""Test VerifyRoutingTableSize invalid inputs."""
with pytest.raises(ValidationError):
VerifyRoutingTableSize.Input(minimum=minimum, maximum=maximum)

View file

@ -20,7 +20,7 @@ from anta.tests.routing.isis import (
VerifyISISSegmentRoutingTunnels,
_get_interface_data,
)
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.routing.ospf import VerifyOSPFMaxLSA, VerifyOSPFNeighborCount, VerifyOSPFNeighborState
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -16,7 +16,7 @@ from anta.tests.aaa import (
VerifyTacacsServers,
VerifyTacacsSourceIntf,
)
from tests.lib.anta import test # noqa: F401; pylint: disable=unused-import
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.avt import VerifyAVTPathHealth, VerifyAVTRole, VerifyAVTSpecificPath
from tests.lib.anta import test # noqa: F401; pylint: disable=unused-import
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -8,10 +8,8 @@ from __future__ import annotations
from typing import Any
# pylint: disable=C0413
# because of the patch above
from anta.tests.bfd import VerifyBFDPeersHealth, VerifyBFDPeersIntervals, VerifyBFDSpecificPeers
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from anta.tests.bfd import VerifyBFDPeersHealth, VerifyBFDPeersIntervals, VerifyBFDPeersRegProtocols, VerifyBFDSpecificPeers
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -163,8 +161,8 @@ DATA: list[dict[str, Any]] = [
"result": "failure",
"messages": [
"Following BFD peers are not configured or timers are not correct:\n"
"{'192.0.255.7': {'default': {'tx_interval': 1300000, 'rx_interval': 1200000, 'multiplier': 4}}, "
"'192.0.255.70': {'MGMT': {'tx_interval': 120000, 'rx_interval': 120000, 'multiplier': 5}}}"
"{'192.0.255.7': {'default': {'tx_interval': 1300, 'rx_interval': 1200, 'multiplier': 4}}, "
"'192.0.255.70': {'MGMT': {'tx_interval': 120, 'rx_interval': 120, 'multiplier': 5}}}"
],
},
},
@ -519,4 +517,133 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyBFDPeersRegProtocols,
"eos_data": [
{
"vrfs": {
"default": {
"ipv4Neighbors": {
"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"],
},
}
}
}
}
},
}
}
],
"inputs": {
"bfd_peers": [
{"peer_address": "192.0.255.7", "vrf": "default", "protocols": ["ospf"]},
{"peer_address": "192.0.255.70", "vrf": "MGMT", "protocols": ["bgp"]},
]
},
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyBFDPeersRegProtocols,
"eos_data": [
{
"vrfs": {
"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"],
},
}
}
}
}
},
}
}
],
"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": [
"The following BFD peers are not configured or have non-registered protocol(s):\n"
"{'192.0.255.7': {'default': ['isis']}, "
"'192.0.255.70': {'MGMT': ['isis']}}"
],
},
},
{
"name": "failure-not-found",
"test": VerifyBFDPeersRegProtocols,
"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": [
"The following BFD peers are not configured or have non-registered protocol(s):\n"
"{'192.0.255.7': {'default': 'Not Configured'}, '192.0.255.70': {'MGMT': 'Not Configured'}}"
],
},
},
]

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.configuration import VerifyRunningConfigDiffs, VerifyRunningConfigLines, VerifyZeroTouch
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -60,14 +60,4 @@ DATA: list[dict[str, Any]] = [
"inputs": {"regex_patterns": ["bla", "bleh"]},
"expected": {"result": "failure", "messages": ["Following patterns were not found: 'bla','bleh'"]},
},
{
"name": "failure-invalid-regex",
"test": VerifyRunningConfigLines,
"eos_data": ["enable password something\nsome other line"],
"inputs": {"regex_patterns": ["["]},
"expected": {
"result": "error",
"messages": ["1 validation error for Input\nregex_patterns.0\n Value error, Invalid regex: unterminated character set at position 0"],
},
},
]

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.connectivity import VerifyLLDPNeighbors, VerifyReachability
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -99,6 +99,28 @@ DATA: list[dict[str, Any]] = [
],
"expected": {"result": "success"},
},
{
"name": "success-df-bit-size",
"test": VerifyReachability,
"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""",
],
},
],
"expected": {"result": "success"},
},
{
"name": "failure-ip",
"test": VerifyReachability,
@ -167,6 +189,28 @@ DATA: list[dict[str, Any]] = [
],
"expected": {"result": "failure", "messages": ["Connectivity test failed for the following source-destination pairs: [('Management0', '10.0.0.11')]"]},
},
{
"name": "failure-size",
"test": VerifyReachability,
"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
""",
],
},
],
"expected": {"result": "failure", "messages": ["Connectivity test failed for the following source-destination pairs: [('Management0', '10.0.0.1')]"]},
},
{
"name": "success",
"test": VerifyLLDPNeighbors,

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.field_notices import VerifyFieldNotice44Resolution, VerifyFieldNotice72Resolution
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -358,8 +358,8 @@ DATA: list[dict[str, Any]] = [
],
"inputs": None,
"expected": {
"result": "error",
"messages": ["Error in running test - FixedSystemvrm1 not found"],
"result": "failure",
"messages": ["Error in running test - Component FixedSystemvrm1 not found in 'show version'"],
},
},
]

View file

@ -0,0 +1,391 @@
# Copyright (c) 2023-2024 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 inputs for anta.tests.flow_tracking."""
from __future__ import annotations
from typing import Any
from anta.tests.flow_tracking import VerifyHardwareFlowTrackerStatus
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
"name": "success",
"test": VerifyHardwareFlowTrackerStatus,
"eos_data": [
{
"trackers": {
"FLOW-TRACKER": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CV-TELEMETRY": {"localIntf": "Loopback0", "templateInterval": 3600000}},
}
},
"running": True,
},
{
"trackers": {
"HARDWARE-TRACKER": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CV-TELEMETRY": {"localIntf": "Loopback0", "templateInterval": 3600000}},
}
},
"running": True,
},
],
"inputs": {"trackers": [{"name": "FLOW-TRACKER"}, {"name": "HARDWARE-TRACKER"}]},
"expected": {"result": "success"},
},
{
"name": "success-with-optional-field",
"test": VerifyHardwareFlowTrackerStatus,
"eos_data": [
{
"trackers": {
"FLOW-TRACKER": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CV-TELEMETRY": {"localIntf": "Loopback0", "templateInterval": 3600000}},
}
},
"running": True,
},
{
"trackers": {
"HARDWARE-TRACKER": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CVP-TELEMETRY": {"localIntf": "Loopback10", "templateInterval": 3600000}},
}
},
"running": True,
},
],
"inputs": {
"trackers": [
{
"name": "FLOW-TRACKER",
"record_export": {"on_inactive_timeout": 60000, "on_interval": 300000},
"exporters": [{"name": "CV-TELEMETRY", "local_interface": "Loopback0", "template_interval": 3600000}],
},
{
"name": "HARDWARE-TRACKER",
"record_export": {"on_inactive_timeout": 60000, "on_interval": 300000},
"exporters": [{"name": "CVP-TELEMETRY", "local_interface": "Loopback10", "template_interval": 3600000}],
},
]
},
"expected": {"result": "success"},
},
{
"name": "failure-flow-tracking-not-running",
"test": VerifyHardwareFlowTrackerStatus,
"eos_data": [{"trackers": {}, "running": False}],
"inputs": {"trackers": [{"name": "FLOW-TRACKER"}]},
"expected": {
"result": "failure",
"messages": ["Hardware flow tracking is not running."],
},
},
{
"name": "failure-tracker-not-configured",
"test": VerifyHardwareFlowTrackerStatus,
"eos_data": [
{
"trackers": {
"HARDWARE-TRACKER": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CVP-TELEMETRY": {"localIntf": "Loopback10", "templateInterval": 3600000}},
}
},
"running": True,
}
],
"inputs": {"trackers": [{"name": "FLOW-Sample"}]},
"expected": {
"result": "failure",
"messages": ["Hardware flow tracker `FLOW-Sample` is not configured."],
},
},
{
"name": "failure-tracker-not-active",
"test": VerifyHardwareFlowTrackerStatus,
"eos_data": [
{
"trackers": {
"FLOW-TRACKER": {
"active": False,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CV-TELEMETRY": {"localIntf": "Loopback0", "templateInterval": 3600000}},
}
},
"running": True,
},
{
"trackers": {
"HARDWARE-TRACKER": {
"active": False,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CVP-TELEMETRY": {"localIntf": "Loopback10", "templateInterval": 3600000}},
}
},
"running": True,
},
],
"inputs": {
"trackers": [
{
"name": "FLOW-TRACKER",
"record_export": {"on_inactive_timeout": 60000, "on_interval": 300000},
"exporters": [{"name": "CV-TELEMETRY", "local_interface": "Loopback0", "template_interval": 3600000}],
},
{
"name": "HARDWARE-TRACKER",
"record_export": {"on_inactive_timeout": 60000, "on_interval": 300000},
"exporters": [{"name": "CVP-TELEMETRY", "local_interface": "Loopback10", "template_interval": 3600000}],
},
]
},
"expected": {
"result": "failure",
"messages": ["Hardware flow tracker `FLOW-TRACKER` is not active.", "Hardware flow tracker `HARDWARE-TRACKER` is not active."],
},
},
{
"name": "failure-incorrect-record-export",
"test": VerifyHardwareFlowTrackerStatus,
"eos_data": [
{
"trackers": {
"FLOW-TRACKER": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CV-TELEMETRY": {"localIntf": "Loopback0", "templateInterval": 3600000}},
}
},
"running": True,
},
{
"trackers": {
"HARDWARE-TRACKER": {
"active": True,
"inactiveTimeout": 6000,
"activeInterval": 30000,
"exporters": {"CVP-TELEMETRY": {"localIntf": "Loopback10", "templateInterval": 3600000}},
}
},
"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},
},
]
},
"expected": {
"result": "failure",
"messages": [
"FLOW-TRACKER: \n"
"Expected `6000` as the inactive timeout, but found `60000` instead.\nExpected `30000` as the interval, but found `300000` instead.\n",
"HARDWARE-TRACKER: \n"
"Expected `60000` as the inactive timeout, but found `6000` instead.\nExpected `300000` as the interval, but found `30000` instead.\n",
],
},
},
{
"name": "failure-incorrect-exporters",
"test": VerifyHardwareFlowTrackerStatus,
"eos_data": [
{
"trackers": {
"FLOW-TRACKER": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {
"CV-TELEMETRY": {"localIntf": "Loopback0", "templateInterval": 3600000},
"CVP-FLOW": {"localIntf": "Loopback0", "templateInterval": 3600000},
},
}
},
"running": True,
},
{
"trackers": {
"HARDWARE-TRACKER": {
"active": True,
"inactiveTimeout": 6000,
"activeInterval": 30000,
"exporters": {
"CVP-TELEMETRY": {"localIntf": "Loopback10", "templateInterval": 3600000},
"Hardware-flow": {"localIntf": "Loopback10", "templateInterval": 3600000},
},
}
},
"running": True,
},
],
"inputs": {
"trackers": [
{
"name": "FLOW-TRACKER",
"exporters": [
{"name": "CV-TELEMETRY", "local_interface": "Loopback0", "template_interval": 3600000},
{"name": "CVP-FLOW", "local_interface": "Loopback10", "template_interval": 3500000},
],
},
{
"name": "HARDWARE-TRACKER",
"exporters": [
{"name": "Hardware-flow", "local_interface": "Loopback99", "template_interval": 3000000},
{"name": "Reverse-flow", "local_interface": "Loopback101", "template_interval": 3000000},
],
},
]
},
"expected": {
"result": "failure",
"messages": [
"FLOW-TRACKER: \n"
"Exporter `CVP-FLOW`: \n"
"Expected `Loopback10` as the local interface, but found `Loopback0` instead.\n"
"Expected `3500000` as the template interval, but found `3600000` instead.\n",
"HARDWARE-TRACKER: \n"
"Exporter `Hardware-flow`: \n"
"Expected `Loopback99` as the local interface, but found `Loopback10` instead.\n"
"Expected `3000000` as the template interval, but found `3600000` instead.\n"
"Exporter `Reverse-flow` is not configured.\n",
],
},
},
{
"name": "failure-all-type",
"test": VerifyHardwareFlowTrackerStatus,
"eos_data": [
{
"trackers": {
"HARDWARE-TRACKER": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CVP-TELEMETRY": {"localIntf": "Loopback10", "templateInterval": 3600000}},
}
},
"running": True,
},
{
"trackers": {
"FLOW-TRIGGER": {
"active": False,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {"CV-TELEMETRY": {"localIntf": "Loopback0", "templateInterval": 3600000}},
}
},
"running": True,
},
{
"trackers": {
"HARDWARE-FLOW": {
"active": True,
"inactiveTimeout": 6000,
"activeInterval": 30000,
"exporters": {"CVP-TELEMETRY": {"localIntf": "Loopback10", "templateInterval": 3600000}},
}
},
"running": True,
},
{
"trackers": {
"FLOW-TRACKER2": {
"active": True,
"inactiveTimeout": 60000,
"activeInterval": 300000,
"exporters": {
"CV-TELEMETRY": {"localIntf": "Loopback0", "templateInterval": 3600000},
"CVP-FLOW": {"localIntf": "Loopback0", "templateInterval": 3600000},
},
}
},
"running": True,
},
{
"trackers": {
"HARDWARE-TRACKER2": {
"active": True,
"inactiveTimeout": 6000,
"activeInterval": 30000,
"exporters": {
"CVP-TELEMETRY": {"localIntf": "Loopback10", "templateInterval": 3600000},
"Hardware-flow": {"localIntf": "Loopback10", "templateInterval": 3600000},
},
}
},
"running": True,
},
],
"inputs": {
"trackers": [
{"name": "FLOW-Sample"},
{
"name": "FLOW-TRIGGER",
"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": "FLOW-TRACKER2",
"exporters": [
{"name": "CV-TELEMETRY", "local_interface": "Loopback0", "template_interval": 3600000},
{"name": "CVP-FLOW", "local_interface": "Loopback10", "template_interval": 3500000},
],
},
{
"name": "HARDWARE-TRACKER2",
"exporters": [
{"name": "Hardware-flow", "local_interface": "Loopback99", "template_interval": 3000000},
{"name": "Reverse-flow", "local_interface": "Loopback101", "template_interval": 3000000},
],
},
]
},
"expected": {
"result": "failure",
"messages": [
"Hardware flow tracker `FLOW-Sample` is not configured.",
"Hardware flow tracker `FLOW-TRIGGER` is not active.",
"HARDWARE-FLOW: \n"
"Expected `60000` as the inactive timeout, but found `6000` instead.\nExpected `300000` as the interval, but found `30000` instead.\n",
"FLOW-TRACKER2: \nExporter `CVP-FLOW`: \n"
"Expected `Loopback10` as the local interface, but found `Loopback0` instead.\n"
"Expected `3500000` as the template interval, but found `3600000` instead.\n",
"HARDWARE-TRACKER2: \nExporter `Hardware-flow`: \n"
"Expected `Loopback99` as the local interface, but found `Loopback10` instead.\n"
"Expected `3000000` as the template interval, but found `3600000` instead.\n"
"Exporter `Reverse-flow` is not configured.\n",
],
},
},
]

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.greent import VerifyGreenT, VerifyGreenTCounters
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -16,7 +16,7 @@ from anta.tests.hardware import (
VerifyTransceiversManufacturers,
VerifyTransceiversTemperature,
)
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -21,12 +21,13 @@ from anta.tests.interfaces import (
VerifyIpVirtualRouterMac,
VerifyL2MTU,
VerifyL3MTU,
VerifyLACPInterfacesStatus,
VerifyLoopbackCount,
VerifyPortChannels,
VerifyStormControlDrops,
VerifySVI,
)
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -651,7 +652,7 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {"threshold": 70.0},
"expected": {
"result": "error",
"result": "failure",
"messages": ["Interface Ethernet1/1 or one of its member interfaces is not Full-Duplex. VerifyInterfaceUtilization has not been implemented."],
},
},
@ -796,7 +797,7 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {"threshold": 70.0},
"expected": {
"result": "error",
"result": "failure",
"messages": ["Interface Port-Channel31 or one of its member interfaces is not Full-Duplex. VerifyInterfaceUtilization has not been implemented."],
},
},
@ -2441,4 +2442,127 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyLACPInterfacesStatus,
"eos_data": [
{
"portChannels": {
"Port-Channel5": {
"interfaces": {
"Ethernet5": {
"actorPortStatus": "bundled",
"partnerPortState": {
"activity": True,
"timeout": False,
"aggregation": True,
"synchronization": True,
"collecting": True,
"distributing": True,
},
"actorPortState": {
"activity": True,
"timeout": False,
"aggregation": True,
"synchronization": True,
"collecting": True,
"distributing": True,
},
}
}
}
},
"interface": "Ethernet5",
"orphanPorts": {},
}
],
"inputs": {"interfaces": [{"name": "Ethernet5", "portchannel": "Port-Channel5"}]},
"expected": {"result": "success"},
},
{
"name": "failure-not-bundled",
"test": VerifyLACPInterfacesStatus,
"eos_data": [
{
"portChannels": {
"Port-Channel5": {
"interfaces": {
"Ethernet5": {
"actorPortStatus": "No Aggregate",
}
}
}
},
"interface": "Ethernet5",
"orphanPorts": {},
}
],
"inputs": {"interfaces": [{"name": "Ethernet5", "portchannel": "Po5"}]},
"expected": {
"result": "failure",
"messages": ["For Interface Ethernet5:\nExpected `bundled` as the local port status, but found `No Aggregate` instead.\n"],
},
},
{
"name": "failure-no-details-found",
"test": VerifyLACPInterfacesStatus,
"eos_data": [
{
"portChannels": {"Port-Channel5": {"interfaces": {}}},
}
],
"inputs": {"interfaces": [{"name": "Ethernet5", "portchannel": "Po 5"}]},
"expected": {
"result": "failure",
"messages": ["Interface 'Ethernet5' is not configured to be a member of LACP 'Port-Channel5'."],
},
},
{
"name": "failure-lacp-params",
"test": VerifyLACPInterfacesStatus,
"eos_data": [
{
"portChannels": {
"Port-Channel5": {
"interfaces": {
"Ethernet5": {
"actorPortStatus": "bundled",
"partnerPortState": {
"activity": False,
"timeout": False,
"aggregation": False,
"synchronization": False,
"collecting": True,
"distributing": True,
},
"actorPortState": {
"activity": False,
"timeout": False,
"aggregation": False,
"synchronization": False,
"collecting": True,
"distributing": True,
},
}
}
}
},
"interface": "Ethernet5",
"orphanPorts": {},
}
],
"inputs": {"interfaces": [{"name": "Ethernet5", "portchannel": "port-channel 5"}]},
"expected": {
"result": "failure",
"messages": [
"For Interface Ethernet5:\n"
"Actor port details:\nExpected `True` as the activity, but found `False` instead."
"\nExpected `True` as the aggregation, but found `False` instead."
"\nExpected `True` as the synchronization, but found `False` instead."
"\nPartner port details:\nExpected `True` as the activity, but found `False` instead.\n"
"Expected `True` as the aggregation, but found `False` instead.\n"
"Expected `True` as the synchronization, but found `False` instead.\n"
],
},
},
]

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.lanz import VerifyLANZ
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -17,7 +17,7 @@ from anta.tests.logging import (
VerifyLoggingSourceIntf,
VerifyLoggingTimestamp,
)
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -201,7 +201,7 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "failure", "messages": ["Logs are not generated with the device FQDN"]},
},
{
"name": "success",
"name": "success-negative-offset",
"test": VerifyLoggingTimestamp,
"eos_data": [
"",
@ -213,6 +213,19 @@ DATA: list[dict[str, Any]] = [
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "success-positive-offset",
"test": VerifyLoggingTimestamp,
"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",
],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyLoggingTimestamp,

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.mlag import VerifyMlagConfigSanity, VerifyMlagDualPrimary, VerifyMlagInterfaces, VerifyMlagPrimaryPriority, VerifyMlagReloadDelay, VerifyMlagStatus
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -110,17 +110,6 @@ DATA: list[dict[str, Any]] = [
"inputs": None,
"expected": {"result": "skipped", "messages": ["MLAG is disabled"]},
},
{
"name": "error",
"test": VerifyMlagConfigSanity,
"eos_data": [
{
"dummy": False,
},
],
"inputs": None,
"expected": {"result": "error", "messages": ["Incorrect JSON response - 'mlagActive' state was not found"]},
},
{
"name": "failure-global",
"test": VerifyMlagConfigSanity,

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.multicast import VerifyIGMPSnoopingGlobal, VerifyIGMPSnoopingVlans
from tests.lib.anta import test # noqa: F401; pylint: disable=unused-import
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.path_selection import VerifyPathsHealth, VerifySpecificPath
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.profiles import VerifyTcamProfile, VerifyUnifiedForwardingTableMode
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.ptp import VerifyPtpGMStatus, VerifyPtpLockStatus, VerifyPtpModeStatus, VerifyPtpOffset, VerifyPtpPortModeStatus
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -295,14 +295,14 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "success"},
},
{
"name": "failure",
"name": "failure-no-interfaces",
"test": VerifyPtpPortModeStatus,
"eos_data": [{"ptpIntfSummaries": {}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["No interfaces are PTP enabled"]},
},
{
"name": "failure",
"name": "failure-invalid-state",
"test": VerifyPtpPortModeStatus,
"eos_data": [
{

View file

@ -7,6 +7,9 @@ from __future__ import annotations
from typing import Any
import pytest
from pydantic import ValidationError
from anta.tests.security import (
VerifyAPIHttpsSSL,
VerifyAPIHttpStatus,
@ -15,6 +18,7 @@ from anta.tests.security import (
VerifyAPISSLCertificate,
VerifyBannerLogin,
VerifyBannerMotd,
VerifyHardwareEntropy,
VerifyIPSecConnHealth,
VerifyIPv4ACL,
VerifySpecificIPSecConn,
@ -23,7 +27,7 @@ from anta.tests.security import (
VerifySSHStatus,
VerifyTelnetStatus,
)
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -38,15 +42,35 @@ DATA: list[dict[str, Any]] = [
"test": VerifySSHStatus,
"eos_data": ["SSH per host connection limit is 20\nFIPS status: disabled\n\n"],
"inputs": None,
"expected": {"result": "error", "messages": ["Could not find SSH status in returned output."]},
"expected": {"result": "failure", "messages": ["Could not find SSH status in returned output."]},
},
{
"name": "failure-ssh-disabled",
"name": "failure-ssh-enabled",
"test": VerifySSHStatus,
"eos_data": ["SSHD status for Default VRF is enabled\nSSH connection limit is 50\nSSH per host connection limit is 20\nFIPS status: disabled\n\n"],
"inputs": None,
"expected": {"result": "failure", "messages": ["SSHD status for Default VRF is enabled"]},
},
{
"name": "success-4.32",
"test": VerifySSHStatus,
"eos_data": [
"User certificate authentication methods: none (neither trusted CA nor SSL profile configured)\n"
"SSHD status for Default VRF: disabled\nSSH connection limit: 50\nSSH per host connection limit: 20\nFIPS status: disabled\n\n"
],
"inputs": None,
"expected": {"result": "success"},
},
{
"name": "failure-ssh-enabled-4.32",
"test": VerifySSHStatus,
"eos_data": [
"User certificate authentication methods: none (neither trusted CA nor SSL profile configured)\n"
"SSHD status for Default VRF: enabled\nSSH connection limit: 50\nSSH per host connection limit: 20\nFIPS status: disabled\n\n"
],
"inputs": None,
"expected": {"result": "failure", "messages": ["SSHD status for Default VRF: enabled"]},
},
{
"name": "success",
"test": VerifySSHIPv4Acl,
@ -580,40 +604,6 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "error-wrong-input-rsa",
"test": VerifyAPISSLCertificate,
"eos_data": [],
"inputs": {
"certificates": [
{
"certificate_name": "ARISTA_ROOT_CA.crt",
"expiry_threshold": 30,
"common_name": "Arista Networks Internal IT Root Cert Authority",
"encryption_algorithm": "RSA",
"key_size": 256,
},
]
},
"expected": {"result": "error", "messages": ["Allowed sizes are (2048, 3072, 4096)."]},
},
{
"name": "error-wrong-input-ecdsa",
"test": VerifyAPISSLCertificate,
"eos_data": [],
"inputs": {
"certificates": [
{
"certificate_name": "ARISTA_SIGNING_CA.crt",
"expiry_threshold": 30,
"common_name": "AristaIT-ICA ECDSA Issuing Cert Authority",
"encryption_algorithm": "ECDSA",
"key_size": 2048,
},
]
},
"expected": {"result": "error", "messages": ["Allowed sizes are (256, 384, 512)."]},
},
{
"name": "success",
"test": VerifyBannerLogin,
@ -1213,4 +1203,84 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyHardwareEntropy,
"eos_data": [{"cpuModel": "2.20GHz", "cryptoModule": "Crypto Module v3.0", "hardwareEntropyEnabled": True, "blockedNetworkProtocols": []}],
"inputs": {},
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyHardwareEntropy,
"eos_data": [{"cpuModel": "2.20GHz", "cryptoModule": "Crypto Module v3.0", "hardwareEntropyEnabled": False, "blockedNetworkProtocols": []}],
"inputs": {},
"expected": {"result": "failure", "messages": ["Hardware entropy generation is disabled."]},
},
]
class TestAPISSLCertificate:
"""Test anta.tests.security.VerifyAPISSLCertificate.Input.APISSLCertificate."""
@pytest.mark.parametrize(
("model_params", "error"),
[
pytest.param(
{
"certificate_name": "ARISTA_ROOT_CA.crt",
"expiry_threshold": 30,
"common_name": "Arista Networks Internal IT Root Cert Authority",
"encryption_algorithm": "RSA",
"key_size": 256,
},
"Value error, `ARISTA_ROOT_CA.crt` key size 256 is invalid for RSA encryption. Allowed sizes are (2048, 3072, 4096).",
id="RSA_wrong_size",
),
pytest.param(
{
"certificate_name": "ARISTA_SIGNING_CA.crt",
"expiry_threshold": 30,
"common_name": "AristaIT-ICA ECDSA Issuing Cert Authority",
"encryption_algorithm": "ECDSA",
"key_size": 2048,
},
"Value error, `ARISTA_SIGNING_CA.crt` key size 2048 is invalid for ECDSA encryption. Allowed sizes are (256, 384, 512).",
id="ECDSA_wrong_size",
),
],
)
def test_invalid(self, model_params: dict[str, Any], error: str) -> None:
"""Test invalid inputs for anta.tests.security.VerifyAPISSLCertificate.Input.APISSLCertificate."""
with pytest.raises(ValidationError) as exec_info:
VerifyAPISSLCertificate.Input.APISSLCertificate.model_validate(model_params)
assert error == exec_info.value.errors()[0]["msg"]
@pytest.mark.parametrize(
"model_params",
[
pytest.param(
{
"certificate_name": "ARISTA_SIGNING_CA.crt",
"expiry_threshold": 30,
"common_name": "AristaIT-ICA ECDSA Issuing Cert Authority",
"encryption_algorithm": "ECDSA",
"key_size": 256,
},
id="ECDSA",
),
pytest.param(
{
"certificate_name": "ARISTA_ROOT_CA.crt",
"expiry_threshold": 30,
"common_name": "Arista Networks Internal IT Root Cert Authority",
"encryption_algorithm": "RSA",
"key_size": 4096,
},
id="RSA",
),
],
)
def test_valid(self, model_params: dict[str, Any]) -> None:
"""Test valid inputs for anta.tests.security.VerifyAPISSLCertificate.Input.APISSLCertificate."""
VerifyAPISSLCertificate.Input.APISSLCertificate.model_validate(model_params)

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.services import VerifyDNSLookup, VerifyDNSServers, VerifyErrdisableRecovery, VerifyHostname
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -7,8 +7,16 @@ from __future__ import annotations
from typing import Any
from anta.tests.snmp import VerifySnmpContact, VerifySnmpIPv4Acl, VerifySnmpIPv6Acl, VerifySnmpLocation, VerifySnmpStatus
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from anta.tests.snmp import (
VerifySnmpContact,
VerifySnmpErrorCounters,
VerifySnmpIPv4Acl,
VerifySnmpIPv6Acl,
VerifySnmpLocation,
VerifySnmpPDUCounters,
VerifySnmpStatus,
)
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -99,6 +107,20 @@ DATA: list[dict[str, Any]] = [
"messages": ["Expected `New York` as the location, but found `Europe` instead."],
},
},
{
"name": "failure-details-not-configured",
"test": VerifySnmpLocation,
"eos_data": [
{
"location": {"location": ""},
}
],
"inputs": {"location": "New York"},
"expected": {
"result": "failure",
"messages": ["SNMP location is not configured."],
},
},
{
"name": "success",
"test": VerifySnmpContact,
@ -124,4 +146,177 @@ DATA: list[dict[str, Any]] = [
"messages": ["Expected `Bob@example.com` as the contact, but found `Jon@example.com` instead."],
},
},
{
"name": "failure-details-not-configured",
"test": VerifySnmpContact,
"eos_data": [
{
"contact": {"contact": ""},
}
],
"inputs": {"contact": "Bob@example.com"},
"expected": {
"result": "failure",
"messages": ["SNMP contact is not configured."],
},
},
{
"name": "success",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {
"inGetPdus": 3,
"inGetNextPdus": 2,
"inSetPdus": 3,
"outGetResponsePdus": 3,
"outTrapPdus": 9,
},
}
],
"inputs": {},
"expected": {"result": "success"},
},
{
"name": "success-specific-pdus",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {
"inGetPdus": 3,
"inGetNextPdus": 0,
"inSetPdus": 0,
"outGetResponsePdus": 0,
"outTrapPdus": 9,
},
}
],
"inputs": {"pdus": ["inGetPdus", "outTrapPdus"]},
"expected": {"result": "success"},
},
{
"name": "failure-counters-not-found",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {},
}
],
"inputs": {},
"expected": {"result": "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,
},
}
],
"inputs": {},
"expected": {
"result": "failure",
"messages": ["The following SNMP PDU counters are not found or have zero PDU counters:\n{'inGetPdus': 0, 'inSetPdus': 0}"],
},
},
{
"name": "failure-pdu-not-found",
"test": VerifySnmpPDUCounters,
"eos_data": [
{
"counters": {
"inGetNextPdus": 0,
"inSetPdus": 0,
"outGetResponsePdus": 0,
},
}
],
"inputs": {"pdus": ["inGetPdus", "outTrapPdus"]},
"expected": {
"result": "failure",
"messages": ["The following SNMP PDU counters are not found or have zero PDU counters:\n{'inGetPdus': 'Not Found', 'outTrapPdus': 'Not Found'}"],
},
},
{
"name": "success",
"test": VerifySnmpErrorCounters,
"eos_data": [
{
"counters": {
"inVersionErrs": 0,
"inBadCommunityNames": 0,
"inBadCommunityUses": 0,
"inParseErrs": 0,
"outTooBigErrs": 0,
"outNoSuchNameErrs": 0,
"outBadValueErrs": 0,
"outGeneralErrs": 0,
},
}
],
"inputs": {},
"expected": {"result": "success"},
},
{
"name": "success-specific-counters",
"test": VerifySnmpErrorCounters,
"eos_data": [
{
"counters": {
"inVersionErrs": 0,
"inBadCommunityNames": 0,
"inBadCommunityUses": 0,
"inParseErrs": 0,
"outTooBigErrs": 5,
"outNoSuchNameErrs": 0,
"outBadValueErrs": 10,
"outGeneralErrs": 1,
},
}
],
"inputs": {"error_counters": ["inVersionErrs", "inParseErrs"]},
"expected": {"result": "success"},
},
{
"name": "failure-counters-not-found",
"test": VerifySnmpErrorCounters,
"eos_data": [
{
"counters": {},
}
],
"inputs": {},
"expected": {"result": "failure", "messages": ["SNMP counters not found."]},
},
{
"name": "failure-incorrect-counters",
"test": VerifySnmpErrorCounters,
"eos_data": [
{
"counters": {
"inVersionErrs": 1,
"inBadCommunityNames": 0,
"inBadCommunityUses": 0,
"inParseErrs": 2,
"outTooBigErrs": 0,
"outNoSuchNameErrs": 0,
"outBadValueErrs": 2,
"outGeneralErrs": 0,
},
}
],
"inputs": {},
"expected": {
"result": "failure",
"messages": [
"The following SNMP error counters are not found or have non-zero error counters:\n{'inVersionErrs': 1, 'inParseErrs': 2, 'outBadValueErrs': 2}"
],
},
},
]

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.software import VerifyEOSExtensions, VerifyEOSVersion, VerifyTerminAttrVersion
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -7,8 +7,8 @@ from __future__ import annotations
from typing import Any
from anta.tests.stp import VerifySTPBlockedPorts, VerifySTPCounters, VerifySTPForwardingPorts, VerifySTPMode, VerifySTPRootPriority
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from anta.tests.stp import VerifySTPBlockedPorts, VerifySTPCounters, VerifySTPForwardingPorts, VerifySTPMode, VerifySTPRootPriority, VerifyStpTopologyChanges
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -324,4 +324,166 @@ DATA: list[dict[str, Any]] = [
"inputs": {"priority": 32768, "instances": [10, 20, 30]},
"expected": {"result": "failure", "messages": ["The following instance(s) have the wrong STP root priority configured: ['VL20', 'VL30']"]},
},
{
"name": "success-mstp",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"Cist": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.7353542},
}
},
"NoStp": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"Ethernet1": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
},
},
},
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
},
{
"name": "success-rstp",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"Cist": {
"interfaces": {
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"PeerEthernet3": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.7353542},
}
},
"NoStp": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"Ethernet1": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
},
},
},
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
},
{
"name": "success-rapid-pvst",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"NoStp": {
"vlans": [4094, 4093, 1006],
"interfaces": {
"PeerEthernet2": {"state": "forwarding", "numChanges": 1, "lastChange": 1727151356.1330667},
},
},
"Vl1": {"vlans": [1], "interfaces": {"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0615358}}},
"Vl10": {
"vlans": [10],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0673406},
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0677001},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0728855},
"Ethernet3": {"state": "forwarding", "numChanges": 3, "lastChange": 1727326730.255137},
},
},
"Vl1198": {
"vlans": [1198],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.074386},
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0743902},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0743942},
},
},
"Vl1199": {
"vlans": [1199],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0744},
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.07453},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.074535},
},
},
"Vl20": {
"vlans": [20],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.073489},
"Vxlan1": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0743747},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0743794},
"Ethernet3": {"state": "forwarding", "numChanges": 3, "lastChange": 1727326730.2551405},
},
},
"Vl3009": {
"vlans": [3009],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.074541},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0745454},
},
},
"Vl3019": {
"vlans": [3019],
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0745502},
"Port-Channel5": {"state": "forwarding", "numChanges": 1, "lastChange": 1727326710.0745537},
},
},
},
},
],
"inputs": {"threshold": 10},
"expected": {"result": "success"},
},
{
"name": "failure-unstable-topology",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"Cist": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.735365},
"Port-Channel5": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
},
},
},
],
"inputs": {"threshold": 10},
"expected": {
"result": "failure",
"messages": [
"The following STP topologies are not configured or number of changes not within the threshold:\n"
"{'topologies': {'Cist': {'Cpu': {'Number of changes': 15}, 'Port-Channel5': {'Number of changes': 15}}}}"
],
},
},
{
"name": "failure-topologies-not-configured",
"test": VerifyStpTopologyChanges,
"eos_data": [
{
"unmappedVlans": [],
"topologies": {
"NoStp": {
"interfaces": {
"Cpu": {"state": "forwarding", "numChanges": 1, "lastChange": 1723990624.735365},
"Ethernet1": {"state": "forwarding", "numChanges": 15, "lastChange": 1723990624.7353542},
}
}
},
},
],
"inputs": {"threshold": 10},
"expected": {"result": "failure", "messages": ["STP is not configured."]},
},
]

View file

@ -7,8 +7,8 @@ from __future__ import annotations
from typing import Any
from anta.tests.stun import VerifyStunClient
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from anta.tests.stun import VerifyStunClient, VerifyStunServer
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -173,4 +173,61 @@ DATA: list[dict[str, Any]] = [
],
},
},
{
"name": "success",
"test": VerifyStunServer,
"eos_data": [
{
"enabled": True,
"pid": 1895,
}
],
"inputs": {},
"expected": {"result": "success"},
},
{
"name": "failure-disabled",
"test": VerifyStunServer,
"eos_data": [
{
"enabled": False,
"pid": 1895,
}
],
"inputs": {},
"expected": {
"result": "failure",
"messages": ["STUN server status is disabled."],
},
},
{
"name": "failure-not-running",
"test": VerifyStunServer,
"eos_data": [
{
"enabled": True,
"pid": 0,
}
],
"inputs": {},
"expected": {
"result": "failure",
"messages": ["STUN server is 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

@ -14,10 +14,11 @@ from anta.tests.system import (
VerifyFileSystemUtilization,
VerifyMemoryUtilization,
VerifyNTP,
VerifyNTPAssociations,
VerifyReloadCause,
VerifyUptime,
)
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -75,13 +76,6 @@ DATA: list[dict[str, Any]] = [
"inputs": None,
"expected": {"result": "failure", "messages": ["Reload cause is: 'Reload after crash.'"]},
},
{
"name": "error",
"test": VerifyReloadCause,
"eos_data": [{}],
"inputs": None,
"expected": {"result": "error", "messages": ["No reload causes available"]},
},
{
"name": "success-without-minidump",
"test": VerifyCoredump,
@ -286,4 +280,186 @@ poll interval unknown
"inputs": None,
"expected": {"result": "failure", "messages": ["The device is not synchronized with the configured NTP server(s): 'unsynchronised'"]},
},
{
"name": "success",
"test": VerifyNTPAssociations,
"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,
},
}
}
],
"inputs": {
"ntp_servers": [
{"server_address": "1.1.1.1", "preferred": True, "stratum": 1},
{"server_address": "2.2.2.2", "stratum": 2},
{"server_address": "3.3.3.3", "stratum": 2},
]
},
"expected": {"result": "success"},
},
{
"name": "success-pool-name",
"test": VerifyNTPAssociations,
"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,
},
}
}
],
"inputs": {
"ntp_servers": [
{"server_address": "1.ntp.networks.com", "preferred": True, "stratum": 1},
{"server_address": "2.ntp.networks.com", "stratum": 2},
{"server_address": "3.ntp.networks.com", "stratum": 2},
]
},
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyNTPAssociations,
"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,
},
}
}
],
"inputs": {
"ntp_servers": [
{"server_address": "1.1.1.1", "preferred": True, "stratum": 1},
{"server_address": "2.2.2.2", "stratum": 2},
{"server_address": "3.3.3.3", "stratum": 2},
]
},
"expected": {
"result": "failure",
"messages": [
"For NTP peer 1.1.1.1:\nExpected `sys.peer` as the condition, but found `candidate` instead.\nExpected `1` as the stratum, but found `2` instead.\n"
"For NTP peer 2.2.2.2:\nExpected `candidate` as the condition, but found `sys.peer` instead.\n"
"For NTP peer 3.3.3.3:\nExpected `candidate` as the condition, but found `sys.peer` instead.\nExpected `2` as the stratum, but found `3` instead."
],
},
},
{
"name": "failure-no-peers",
"test": VerifyNTPAssociations,
"eos_data": [{"peers": {}}],
"inputs": {
"ntp_servers": [
{"server_address": "1.1.1.1", "preferred": True, "stratum": 1},
{"server_address": "2.2.2.2", "stratum": 1},
{"server_address": "3.3.3.3", "stratum": 1},
]
},
"expected": {
"result": "failure",
"messages": ["None of NTP peers are not configured."],
},
},
{
"name": "failure-one-peer-not-found",
"test": VerifyNTPAssociations,
"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,
},
}
}
],
"inputs": {
"ntp_servers": [
{"server_address": "1.1.1.1", "preferred": True, "stratum": 1},
{"server_address": "2.2.2.2", "stratum": 1},
{"server_address": "3.3.3.3", "stratum": 1},
]
},
"expected": {
"result": "failure",
"messages": ["NTP peer 3.3.3.3 is 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,
}
}
}
],
"inputs": {
"ntp_servers": [
{"server_address": "1.1.1.1", "preferred": True, "stratum": 1},
{"server_address": "2.2.2.2", "stratum": 1},
{"server_address": "3.3.3.3", "stratum": 1},
]
},
"expected": {
"result": "failure",
"messages": [
"For NTP peer 1.1.1.1:\nExpected `sys.peer` as the condition, but found `candidate` instead.\n"
"NTP peer 2.2.2.2 is not configured.\nNTP peer 3.3.3.3 is not configured."
],
},
},
]

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.vlan import VerifyVlanInternalPolicy
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{

View file

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Any
from anta.tests.vxlan import VerifyVxlan1ConnSettings, VerifyVxlan1Interface, VerifyVxlanConfigSanity, VerifyVxlanVniBinding, VerifyVxlanVtep
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
from tests.units.anta_tests import test
DATA: list[dict[str, Any]] = [
{
@ -26,21 +26,21 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "skipped", "messages": ["Vxlan1 interface is not configured"]},
},
{
"name": "failure",
"name": "failure-down-up",
"test": VerifyVxlan1Interface,
"eos_data": [{"interfaceDescriptions": {"Vxlan1": {"lineProtocolStatus": "down", "interfaceStatus": "up"}}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Vxlan1 interface is down/up"]},
},
{
"name": "failure",
"name": "failure-up-down",
"test": VerifyVxlan1Interface,
"eos_data": [{"interfaceDescriptions": {"Vxlan1": {"lineProtocolStatus": "up", "interfaceStatus": "down"}}}],
"inputs": None,
"expected": {"result": "failure", "messages": ["Vxlan1 interface is up/down"]},
},
{
"name": "failure",
"name": "failure-down-down",
"test": VerifyVxlan1Interface,
"eos_data": [{"interfaceDescriptions": {"Vxlan1": {"lineProtocolStatus": "down", "interfaceStatus": "down"}}}],
"inputs": None,