summaryrefslogtreecommitdiffstats
path: root/tests/units/anta_tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/units/anta_tests')
-rw-r--r--tests/units/anta_tests/routing/test_bgp.py10
-rw-r--r--tests/units/anta_tests/routing/test_isis.py570
-rw-r--r--tests/units/anta_tests/test_aaa.py12
-rw-r--r--tests/units/anta_tests/test_avt.py581
-rw-r--r--tests/units/anta_tests/test_configuration.py41
-rw-r--r--tests/units/anta_tests/test_field_notices.py22
-rw-r--r--tests/units/anta_tests/test_interfaces.py288
-rw-r--r--tests/units/anta_tests/test_logging.py14
-rw-r--r--tests/units/anta_tests/test_path_selection.py327
-rw-r--r--tests/units/anta_tests/test_ptp.py14
-rw-r--r--tests/units/anta_tests/test_security.py43
-rw-r--r--tests/units/anta_tests/test_services.py2
-rw-r--r--tests/units/anta_tests/test_software.py11
13 files changed, 1918 insertions, 17 deletions
diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py
index 31006c5..e712e12 100644
--- a/tests/units/anta_tests/routing/test_bgp.py
+++ b/tests/units/anta_tests/routing/test_bgp.py
@@ -30,6 +30,7 @@ DATA: list[dict[str, Any]] = [
"name": "success",
"test": VerifyBGPPeerCount,
"eos_data": [
+ # Need to order the output as the commands would be sorted after template rendering.
{
"vrfs": {
"default": {
@@ -120,9 +121,10 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {
"address_families": [
+ # evpn first to make sure that the correct mapping output to input is kept.
+ {"afi": "evpn", "num_peers": 2},
{"afi": "ipv4", "safi": "unicast", "vrf": "default", "num_peers": 2},
{"afi": "ipv4", "safi": "sr-te", "vrf": "MGMT", "num_peers": 1},
- {"afi": "evpn", "num_peers": 2},
{"afi": "link-state", "num_peers": 2},
{"afi": "path-selection", "num_peers": 2},
]
@@ -652,9 +654,10 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {
"address_families": [
+ # Path selection first to make sure input to output mapping is correct.
+ {"afi": "path-selection"},
{"afi": "ipv4", "safi": "unicast", "vrf": "default"},
{"afi": "ipv4", "safi": "sr-te", "vrf": "MGMT"},
- {"afi": "path-selection"},
{"afi": "link-state"},
]
},
@@ -1081,6 +1084,8 @@ DATA: list[dict[str, Any]] = [
],
"inputs": {
"address_families": [
+ # Path selection first to make sure input to output mapping is correct.
+ {"afi": "path-selection", "peers": ["10.1.255.20", "10.1.255.22"]},
{
"afi": "ipv4",
"safi": "unicast",
@@ -1093,7 +1098,6 @@ DATA: list[dict[str, Any]] = [
"vrf": "MGMT",
"peers": ["10.1.255.10", "10.1.255.12"],
},
- {"afi": "path-selection", "peers": ["10.1.255.20", "10.1.255.22"]},
{"afi": "link-state", "peers": ["10.1.255.30", "10.1.255.32"]},
]
},
diff --git a/tests/units/anta_tests/routing/test_isis.py b/tests/units/anta_tests/routing/test_isis.py
new file mode 100644
index 0000000..ec41105
--- /dev/null
+++ b/tests/units/anta_tests/routing/test_isis.py
@@ -0,0 +1,570 @@
+# 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.
+"""Tests for anta.tests.routing.ospf.py."""
+
+from __future__ import annotations
+
+from typing import Any
+
+from anta.tests.routing.isis import VerifyISISInterfaceMode, VerifyISISNeighborCount, VerifyISISNeighborState
+from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
+
+DATA: list[dict[str, Any]] = [
+ {
+ "name": "success only default vrf",
+ "test": VerifyISISNeighborState,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "neighbors": {
+ "0168.0000.0111": {
+ "adjacencies": [
+ {
+ "hostname": "s1-p01",
+ "circuitId": "83",
+ "interfaceName": "Ethernet1",
+ "state": "up",
+ "lastHelloTime": 1713688408,
+ "routerIdV4": "1.0.0.111",
+ }
+ ]
+ },
+ "0168.0000.0112": {
+ "adjacencies": [
+ {
+ "hostname": "s1-p02",
+ "circuitId": "87",
+ "interfaceName": "Ethernet2",
+ "state": "up",
+ "lastHelloTime": 1713688405,
+ "routerIdV4": "1.0.0.112",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": None,
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "success different vrfs",
+ "test": VerifyISISNeighborState,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "neighbors": {
+ "0168.0000.0111": {
+ "adjacencies": [
+ {
+ "hostname": "s1-p01",
+ "circuitId": "83",
+ "interfaceName": "Ethernet1",
+ "state": "up",
+ "lastHelloTime": 1713688408,
+ "routerIdV4": "1.0.0.111",
+ }
+ ]
+ },
+ },
+ },
+ },
+ "customer": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "neighbors": {
+ "0168.0000.0112": {
+ "adjacencies": [
+ {
+ "hostname": "s1-p02",
+ "circuitId": "87",
+ "interfaceName": "Ethernet2",
+ "state": "up",
+ "lastHelloTime": 1713688405,
+ "routerIdV4": "1.0.0.112",
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ ],
+ "inputs": None,
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure",
+ "test": VerifyISISNeighborState,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "neighbors": {
+ "0168.0000.0111": {
+ "adjacencies": [
+ {
+ "hostname": "s1-p01",
+ "circuitId": "83",
+ "interfaceName": "Ethernet1",
+ "state": "down",
+ "lastHelloTime": 1713688408,
+ "routerIdV4": "1.0.0.111",
+ }
+ ]
+ },
+ "0168.0000.0112": {
+ "adjacencies": [
+ {
+ "hostname": "s1-p02",
+ "circuitId": "87",
+ "interfaceName": "Ethernet2",
+ "state": "up",
+ "lastHelloTime": 1713688405,
+ "routerIdV4": "1.0.0.112",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": None,
+ "expected": {
+ "result": "failure",
+ "messages": ["Some neighbors are not in the correct state (UP): [{'vrf': 'default', 'instance': 'CORE-ISIS', 'neighbor': 's1-p01', 'state': 'down'}]."],
+ },
+ },
+ {
+ "name": "success only default vrf",
+ "test": VerifyISISNeighborCount,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "interfaces": {
+ "Loopback0": {
+ "enabled": True,
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": True,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "areaProxyBoundary": False,
+ },
+ "Ethernet1": {
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 1,
+ "linkId": "84",
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": False,
+ "v4Protection": "link",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ "Ethernet2": {
+ "enabled": True,
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 1,
+ "linkId": "88",
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": False,
+ "v4Protection": "link",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Ethernet1", "level": 2, "count": 1},
+ {"name": "Ethernet2", "level": 2, "count": 1},
+ ]
+ },
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "success VerifyISISInterfaceMode only default vrf",
+ "test": VerifyISISInterfaceMode,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "interfaces": {
+ "Loopback0": {
+ "enabled": True,
+ "index": 2,
+ "snpa": "0:0:0:0:0:0",
+ "mtu": 65532,
+ "interfaceAddressFamily": "ipv4",
+ "interfaceType": "loopback",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": True,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "areaProxyBoundary": False,
+ },
+ "Ethernet1": {
+ "enabled": True,
+ "index": 132,
+ "snpa": "P2P",
+ "interfaceType": "point-to-point",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 1,
+ "linkId": "84",
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": False,
+ "v4Protection": "link",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ "Ethernet2": {
+ "enabled": True,
+ "interfaceType": "broadcast",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 0,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": True,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Loopback0", "mode": "passive"},
+ {"name": "Ethernet2", "mode": "passive"},
+ {"name": "Ethernet1", "mode": "point-to-point", "vrf": "default"},
+ ]
+ },
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure VerifyISISInterfaceMode default vrf with interface not running passive mode",
+ "test": VerifyISISInterfaceMode,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "interfaces": {
+ "Loopback0": {
+ "enabled": True,
+ "index": 2,
+ "snpa": "0:0:0:0:0:0",
+ "mtu": 65532,
+ "interfaceAddressFamily": "ipv4",
+ "interfaceType": "loopback",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": True,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "areaProxyBoundary": False,
+ },
+ "Ethernet1": {
+ "enabled": True,
+ "index": 132,
+ "snpa": "P2P",
+ "interfaceType": "point-to-point",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 1,
+ "linkId": "84",
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": False,
+ "v4Protection": "link",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ "Ethernet2": {
+ "enabled": True,
+ "interfaceType": "point-to-point",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 0,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": False,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Loopback0", "mode": "passive"},
+ {"name": "Ethernet2", "mode": "passive"},
+ {"name": "Ethernet1", "mode": "point-to-point", "vrf": "default"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": ["Interface Ethernet2 in VRF default is not running in passive mode"],
+ },
+ },
+ {
+ "name": "failure VerifyISISInterfaceMode default vrf with interface not running point-point mode",
+ "test": VerifyISISInterfaceMode,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "interfaces": {
+ "Loopback0": {
+ "enabled": True,
+ "index": 2,
+ "snpa": "0:0:0:0:0:0",
+ "mtu": 65532,
+ "interfaceAddressFamily": "ipv4",
+ "interfaceType": "loopback",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": True,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "areaProxyBoundary": False,
+ },
+ "Ethernet1": {
+ "enabled": True,
+ "index": 132,
+ "snpa": "P2P",
+ "interfaceType": "broadcast",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 1,
+ "linkId": "84",
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": False,
+ "v4Protection": "link",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ "Ethernet2": {
+ "enabled": True,
+ "interfaceType": "broadcast",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 0,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": True,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Loopback0", "mode": "passive"},
+ {"name": "Ethernet2", "mode": "passive"},
+ {"name": "Ethernet1", "mode": "point-to-point", "vrf": "default"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": ["Interface Ethernet1 in VRF default is not running in point-to-point reporting broadcast"],
+ },
+ },
+ {
+ "name": "failure VerifyISISInterfaceMode default vrf with interface not running correct VRF mode",
+ "test": VerifyISISInterfaceMode,
+ "eos_data": [
+ {
+ "vrfs": {
+ "fake_vrf": {
+ "isisInstances": {
+ "CORE-ISIS": {
+ "interfaces": {
+ "Loopback0": {
+ "enabled": True,
+ "index": 2,
+ "snpa": "0:0:0:0:0:0",
+ "mtu": 65532,
+ "interfaceAddressFamily": "ipv4",
+ "interfaceType": "loopback",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": True,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "areaProxyBoundary": False,
+ },
+ "Ethernet1": {
+ "enabled": True,
+ "index": 132,
+ "snpa": "P2P",
+ "interfaceType": "point-to-point",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 1,
+ "linkId": "84",
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": False,
+ "v4Protection": "link",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ "Ethernet2": {
+ "enabled": True,
+ "interfaceType": "broadcast",
+ "intfLevels": {
+ "2": {
+ "ipv4Metric": 10,
+ "numAdjacencies": 0,
+ "sharedSecretProfile": "",
+ "isisAdjacencies": [],
+ "passive": True,
+ "v4Protection": "disabled",
+ "v6Protection": "disabled",
+ }
+ },
+ "interfaceSpeed": 1000,
+ "areaProxyBoundary": False,
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Loopback0", "mode": "passive"},
+ {"name": "Ethernet2", "mode": "passive"},
+ {"name": "Ethernet1", "mode": "point-to-point", "vrf": "default"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "Interface Loopback0 not found in VRF default",
+ "Interface Ethernet2 not found in VRF default",
+ "Interface Ethernet1 not found in VRF default",
+ ],
+ },
+ },
+]
diff --git a/tests/units/anta_tests/test_aaa.py b/tests/units/anta_tests/test_aaa.py
index f0324c5..40bf82e 100644
--- a/tests/units/anta_tests/test_aaa.py
+++ b/tests/units/anta_tests/test_aaa.py
@@ -272,6 +272,18 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "success"},
},
{
+ "name": "success-skipping-exec",
+ "test": VerifyAuthzMethods,
+ "eos_data": [
+ {
+ "commandsAuthzMethods": {"privilege0-15": {"methods": ["group tacacs+", "local"]}},
+ "execAuthzMethods": {"exec": {"methods": ["group tacacs+", "local"]}},
+ },
+ ],
+ "inputs": {"methods": ["tacacs+", "local"], "types": ["commands"]},
+ "expected": {"result": "success"},
+ },
+ {
"name": "failure-commands",
"test": VerifyAuthzMethods,
"eos_data": [
diff --git a/tests/units/anta_tests/test_avt.py b/tests/units/anta_tests/test_avt.py
new file mode 100644
index 0000000..7ef6be3
--- /dev/null
+++ b/tests/units/anta_tests/test_avt.py
@@ -0,0 +1,581 @@
+# 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.
+"""Tests for anta.tests.avt.py."""
+
+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
+
+DATA: list[dict[str, Any]] = [
+ {
+ "name": "success",
+ "test": VerifyAVTPathHealth,
+ "eos_data": [
+ {
+ "vrfs": {
+ "data": {
+ "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},
+ },
+ }
+ }
+ }
+ },
+ "guest": {
+ "avts": {
+ "GUEST-AVT-POLICY-DEFAULT": {
+ "avtPaths": {
+ "direct:10": {
+ "flags": {"directPath": True, "valid": True, "active": True},
+ },
+ "direct:8": {
+ "flags": {"directPath": True, "valid": True, "active": True},
+ },
+ }
+ }
+ }
+ },
+ "default": {
+ "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},
+ },
+ }
+ },
+ "DEFAULT-AVT-POLICY-DEFAULT": {
+ "avtPaths": {
+ "direct:10": {
+ "flags": {"directPath": True, "valid": True, "active": True},
+ },
+ "direct:8": {
+ "flags": {"directPath": True, "valid": True, "active": True},
+ },
+ }
+ },
+ }
+ },
+ }
+ }
+ ],
+ "inputs": {},
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure-avt-not-configured",
+ "test": VerifyAVTPathHealth,
+ "eos_data": [{"vrfs": {}}],
+ "inputs": {},
+ "expected": {
+ "result": "failure",
+ "messages": ["Adaptive virtual topology paths are not configured."],
+ },
+ },
+ {
+ "name": "failure-not-active-path",
+ "test": VerifyAVTPathHealth,
+ "eos_data": [
+ {
+ "vrfs": {
+ "data": {
+ "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},
+ },
+ }
+ }
+ }
+ },
+ "guest": {
+ "avts": {
+ "GUEST-AVT-POLICY-DEFAULT": {
+ "avtPaths": {
+ "direct:10": {
+ "flags": {"directPath": True, "valid": True, "active": False},
+ },
+ "direct:8": {
+ "flags": {"directPath": True, "valid": True, "active": True},
+ },
+ }
+ }
+ }
+ },
+ "default": {
+ "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},
+ },
+ }
+ },
+ "DEFAULT-AVT-POLICY-DEFAULT": {
+ "avtPaths": {
+ "direct:10": {
+ "flags": {"directPath": True, "valid": True, "active": False},
+ },
+ "direct:8": {
+ "flags": {"directPath": True, "valid": True, "active": True},
+ },
+ }
+ },
+ }
+ },
+ }
+ }
+ ],
+ "inputs": {},
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "AVT path direct:10 for profile GUEST-AVT-POLICY-DEFAULT in VRF guest is not active.",
+ "AVT path direct:1 for profile CONTROL-PLANE-PROFILE in VRF default is not active.",
+ "AVT path direct:10 for profile DEFAULT-AVT-POLICY-DEFAULT in VRF default is not active.",
+ ],
+ },
+ },
+ {
+ "name": "failure-invalid-path",
+ "test": VerifyAVTPathHealth,
+ "eos_data": [
+ {
+ "vrfs": {
+ "data": {
+ "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},
+ },
+ }
+ }
+ }
+ },
+ "guest": {
+ "avts": {
+ "GUEST-AVT-POLICY-DEFAULT": {
+ "avtPaths": {
+ "direct:10": {
+ "flags": {"directPath": True, "valid": True, "active": True},
+ },
+ "direct:8": {
+ "flags": {"directPath": True, "valid": False, "active": True},
+ },
+ }
+ }
+ }
+ },
+ "default": {
+ "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},
+ },
+ }
+ },
+ "DEFAULT-AVT-POLICY-DEFAULT": {
+ "avtPaths": {
+ "direct:10": {
+ "flags": {"directPath": True, "valid": True, "active": True},
+ },
+ "direct:8": {
+ "flags": {"directPath": True, "valid": False, "active": True},
+ },
+ }
+ },
+ }
+ },
+ }
+ }
+ ],
+ "inputs": {},
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "AVT path direct:10 for profile DATA-AVT-POLICY-DEFAULT in VRF data is invalid.",
+ "AVT path direct:8 for profile GUEST-AVT-POLICY-DEFAULT in VRF guest is invalid.",
+ "AVT path direct:10 for profile CONTROL-PLANE-PROFILE in VRF default is invalid.",
+ "AVT path direct:8 for profile DEFAULT-AVT-POLICY-DEFAULT in VRF default is invalid.",
+ ],
+ },
+ },
+ {
+ "name": "failure-not-active-and-invalid",
+ "test": VerifyAVTPathHealth,
+ "eos_data": [
+ {
+ "vrfs": {
+ "data": {
+ "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},
+ },
+ }
+ }
+ }
+ },
+ "guest": {
+ "avts": {
+ "GUEST-AVT-POLICY-DEFAULT": {
+ "avtPaths": {
+ "direct:10": {
+ "flags": {"directPath": True, "valid": False, "active": True},
+ },
+ "direct:8": {
+ "flags": {"directPath": True, "valid": False, "active": False},
+ },
+ }
+ }
+ }
+ },
+ "default": {
+ "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},
+ },
+ }
+ },
+ "DEFAULT-AVT-POLICY-DEFAULT": {
+ "avtPaths": {
+ "direct:10": {
+ "flags": {"directPath": True, "valid": True, "active": False},
+ },
+ "direct:8": {
+ "flags": {"directPath": True, "valid": False, "active": False},
+ },
+ }
+ },
+ }
+ },
+ }
+ }
+ ],
+ "inputs": {},
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "AVT path direct:10 for profile DATA-AVT-POLICY-DEFAULT in VRF data is invalid and not active.",
+ "AVT path direct:1 for profile DATA-AVT-POLICY-DEFAULT in VRF data is not active.",
+ "AVT path direct:10 for profile GUEST-AVT-POLICY-DEFAULT in VRF guest is invalid.",
+ "AVT path direct:8 for profile GUEST-AVT-POLICY-DEFAULT in VRF guest is invalid and not active.",
+ "AVT path direct:10 for profile CONTROL-PLANE-PROFILE in VRF default is invalid and not active.",
+ "AVT path direct:10 for profile DEFAULT-AVT-POLICY-DEFAULT in VRF default is not active.",
+ "AVT path direct:8 for profile DEFAULT-AVT-POLICY-DEFAULT in VRF default is invalid and not active.",
+ ],
+ },
+ },
+ {
+ "name": "success",
+ "test": VerifyAVTSpecificPath,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "avts": {
+ "DEFAULT-AVT-POLICY-CONTROL-PLANE": {
+ "avtPaths": {
+ "direct:10": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "direct:9": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "multihop:1": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "multihop:3": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "vrfs": {
+ "data": {
+ "avts": {
+ "DATA-AVT-POLICY-CONTROL-PLANE": {
+ "avtPaths": {
+ "direct:10": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "direct:9": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "direct:8": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:1": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:3": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "vrfs": {
+ "data": {
+ "avts": {
+ "DATA-AVT-POLICY-CONTROL-PLANE": {
+ "avtPaths": {
+ "direct:10": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "direct:9": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "direct:8": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:1": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:3": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": {
+ "avt_paths": [
+ {"avt_name": "DEFAULT-AVT-POLICY-CONTROL-PLANE", "destination": "10.101.255.2", "next_hop": "10.101.255.1", "path_type": "multihop"},
+ {"avt_name": "DATA-AVT-POLICY-CONTROL-PLANE", "vrf": "data", "destination": "10.101.255.1", "next_hop": "10.101.255.2", "path_type": "direct"},
+ {"avt_name": "DATA-AVT-POLICY-CONTROL-PLANE", "vrf": "data", "destination": "10.101.255.1", "next_hop": "10.101.255.2"},
+ ]
+ },
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure-no-peer",
+ "test": VerifyAVTSpecificPath,
+ "eos_data": [
+ {"vrfs": {}},
+ {
+ "vrfs": {
+ "data": {
+ "avts": {
+ "DATA-AVT-POLICY-CONTROL-PLANE": {
+ "avtPaths": {
+ "direct:10": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "direct:9": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "multihop:1": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:3": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "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"},
+ {"avt_name": "DATA-AVT-POLICY-CONTROL-PLANE", "vrf": "data", "destination": "10.101.255.1", "next_hop": "10.101.255.2", "path_type": "multihop"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": ["AVT configuration for peer '10.101.255.2' under topology 'MGMT-AVT-POLICY-DEFAULT' in VRF 'default' is not found."],
+ },
+ },
+ {
+ "name": "failure-no-path-with-correct-next-hop",
+ "test": VerifyAVTSpecificPath,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "avts": {
+ "DEFAULT-AVT-POLICY-CONTROL-PLANE": {
+ "avtPaths": {
+ "direct:10": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "direct:9": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:1": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "multihop:3": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "vrfs": {
+ "data": {
+ "avts": {
+ "DATA-AVT-POLICY-CONTROL-PLANE": {
+ "avtPaths": {
+ "direct:10": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "direct:9": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "multihop:1": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:3": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": {
+ "avt_paths": [
+ {
+ "avt_name": "DEFAULT-AVT-POLICY-CONTROL-PLANE",
+ "vrf": "default",
+ "destination": "10.101.255.2",
+ "next_hop": "10.101.255.11",
+ "path_type": "multihop",
+ },
+ {"avt_name": "DATA-AVT-POLICY-CONTROL-PLANE", "vrf": "data", "destination": "10.101.255.1", "next_hop": "10.101.255.21", "path_type": "direct"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "No 'multihop' path found with next-hop address '10.101.255.11' for AVT peer '10.101.255.2' under "
+ "topology 'DEFAULT-AVT-POLICY-CONTROL-PLANE' in VRF 'default'.",
+ "No 'direct' path found with next-hop address '10.101.255.21' for AVT peer '10.101.255.1' under "
+ "topology 'DATA-AVT-POLICY-CONTROL-PLANE' in VRF 'data'.",
+ ],
+ },
+ },
+ {
+ "name": "failure-incorrect-path",
+ "test": VerifyAVTSpecificPath,
+ "eos_data": [
+ {
+ "vrfs": {
+ "default": {
+ "avts": {
+ "DEFAULT-AVT-POLICY-CONTROL-PLANE": {
+ "avtPaths": {
+ "direct:10": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "direct:9": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:1": {"flags": {"directPath": True, "valid": False, "active": False}, "nexthopAddr": "10.101.255.1"},
+ "multihop:3": {"flags": {"directPath": False, "valid": True, "active": False}, "nexthopAddr": "10.101.255.1"},
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "vrfs": {
+ "data": {
+ "avts": {
+ "DATA-AVT-POLICY-CONTROL-PLANE": {
+ "avtPaths": {
+ "direct:10": {"flags": {"directPath": True, "valid": True, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "direct:9": {"flags": {"directPath": True, "valid": False, "active": True}, "nexthopAddr": "10.101.255.1"},
+ "multihop:1": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ "multihop:3": {"flags": {"directPath": False, "valid": True, "active": True}, "nexthopAddr": "10.101.255.2"},
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": {
+ "avt_paths": [
+ {
+ "avt_name": "DEFAULT-AVT-POLICY-CONTROL-PLANE",
+ "vrf": "default",
+ "destination": "10.101.255.2",
+ "next_hop": "10.101.255.1",
+ "path_type": "multihop",
+ },
+ {"avt_name": "DATA-AVT-POLICY-CONTROL-PLANE", "vrf": "data", "destination": "10.101.255.1", "next_hop": "10.101.255.1", "path_type": "direct"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "AVT path 'multihop:3' for topology 'DEFAULT-AVT-POLICY-CONTROL-PLANE' in VRF 'default' is inactive.",
+ "AVT path 'direct:9' for topology 'DATA-AVT-POLICY-CONTROL-PLANE' in VRF 'data' is invalid.",
+ ],
+ },
+ },
+ {
+ "name": "success",
+ "test": VerifyAVTRole,
+ "eos_data": [{"role": "edge"}],
+ "inputs": {"role": "edge"},
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure-incorrect-role",
+ "test": VerifyAVTRole,
+ "eos_data": [{"role": "transit"}],
+ "inputs": {"role": "edge"},
+ "expected": {"result": "failure", "messages": ["Expected AVT role as `edge`, but found `transit` instead."]},
+ },
+]
diff --git a/tests/units/anta_tests/test_configuration.py b/tests/units/anta_tests/test_configuration.py
index 0444db6..7f198a3 100644
--- a/tests/units/anta_tests/test_configuration.py
+++ b/tests/units/anta_tests/test_configuration.py
@@ -7,7 +7,7 @@ from __future__ import annotations
from typing import Any
-from anta.tests.configuration import VerifyRunningConfigDiffs, VerifyZeroTouch
+from anta.tests.configuration import VerifyRunningConfigDiffs, VerifyRunningConfigLines, VerifyZeroTouch
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611
DATA: list[dict[str, Any]] = [
@@ -32,5 +32,42 @@ DATA: list[dict[str, Any]] = [
"inputs": None,
"expected": {"result": "success"},
},
- {"name": "failure", "test": VerifyRunningConfigDiffs, "eos_data": ["blah blah"], "inputs": None, "expected": {"result": "failure", "messages": ["blah blah"]}},
+ {
+ "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,
+ "eos_data": ["enable password something\nsome other line"],
+ "inputs": {"regex_patterns": ["^enable password .*$", "^.*other line$"]},
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure",
+ "test": VerifyRunningConfigLines,
+ "eos_data": ["enable password something\nsome other line"],
+ "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"],
+ },
+ },
]
diff --git a/tests/units/anta_tests/test_field_notices.py b/tests/units/anta_tests/test_field_notices.py
index 66e7801..3cb7286 100644
--- a/tests/units/anta_tests/test_field_notices.py
+++ b/tests/units/anta_tests/test_field_notices.py
@@ -21,7 +21,7 @@ DATA: list[dict[str, Any]] = [
"modelName": "DCS-7280QRA-C36S",
"details": {
"deviations": [],
- "components": [{"name": "Aboot", "version": "Aboot-veos-8.0.0-3255441"}],
+ "components": [{"name": "Aboot", "version": "Aboot-veos-8.0.0-3255441"}, {"name": "NotAboot", "version": "Aboot-veos-8.0.0-3255441"}],
},
},
],
@@ -129,6 +129,26 @@ DATA: list[dict[str, Any]] = [
},
},
{
+ "name": "failure-no-aboot-component",
+ "test": VerifyFieldNotice44Resolution,
+ "eos_data": [
+ {
+ "imageFormatVersion": "1.0",
+ "uptime": 1109144.35,
+ "modelName": "DCS-7280QRA-C36S",
+ "details": {
+ "deviations": [],
+ "components": [{"name": "NotAboot", "version": "Aboot-veos-4.0.1-3255441"}],
+ },
+ },
+ ],
+ "inputs": None,
+ "expected": {
+ "result": "failure",
+ "messages": ["Aboot component not found"],
+ },
+ },
+ {
"name": "success-JPE",
"test": VerifyFieldNotice72Resolution,
"eos_data": [
diff --git a/tests/units/anta_tests/test_interfaces.py b/tests/units/anta_tests/test_interfaces.py
index 58f568f..b8cf493 100644
--- a/tests/units/anta_tests/test_interfaces.py
+++ b/tests/units/anta_tests/test_interfaces.py
@@ -1,7 +1,7 @@
# 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.hardware."""
+"""Test inputs for anta.tests.interfaces."""
# pylint: disable=C0302
from __future__ import annotations
@@ -14,6 +14,7 @@ from anta.tests.interfaces import (
VerifyInterfaceErrDisabled,
VerifyInterfaceErrors,
VerifyInterfaceIPv4,
+ VerifyInterfacesSpeed,
VerifyInterfacesStatus,
VerifyInterfaceUtilization,
VerifyIPProxyARP,
@@ -1354,6 +1355,14 @@ DATA: list[dict[str, Any]] = [
"lineProtocolStatus": "up",
"mtu": 65535,
},
+ # Checking not loopbacks are skipped
+ "Ethernet666": {
+ "name": "Ethernet666",
+ "interfaceStatus": "connected",
+ "interfaceAddress": {"ipAddr": {"maskLen": 32, "address": "6.6.6.6"}},
+ "ipv4Routable240": False,
+ "lineProtocolStatus": "up",
+ },
},
},
],
@@ -1733,7 +1742,7 @@ DATA: list[dict[str, Any]] = [
},
},
],
- "inputs": {"mtu": 9214},
+ "inputs": {"mtu": 9214, "ignored_interfaces": ["Loopback", "Port-Channel", "Management", "Vxlan"], "specific_mtu": [{"Ethernet10": 9214}]},
"expected": {"result": "success"},
},
{
@@ -2157,4 +2166,279 @@ DATA: list[dict[str, Any]] = [
"inputs": {"mac_address": "00:1c:73:00:dc:01"},
"expected": {"result": "failure", "messages": ["IP virtual router MAC address `00:1c:73:00:dc:01` is not configured."]},
},
+ {
+ "name": "success",
+ "test": VerifyInterfacesSpeed,
+ "eos_data": [
+ {
+ "interfaces": {
+ "Ethernet1": {
+ "bandwidth": 1000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 2,
+ },
+ "Ethernet1/1/2": {
+ "bandwidth": 1000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 2,
+ },
+ "Ethernet3": {
+ "bandwidth": 100000000000,
+ "autoNegotiate": "success",
+ "duplex": "duplexFull",
+ "lanes": 8,
+ },
+ "Ethernet4": {
+ "bandwidth": 2500000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 8,
+ },
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Ethernet1", "auto": False, "speed": 1},
+ {"name": "Ethernet1", "auto": False, "speed": 1, "lanes": 2},
+ {"name": "Ethernet1/1/2", "auto": False, "speed": 1},
+ {"name": "Ethernet3", "auto": True, "speed": 100},
+ {"name": "Ethernet3", "auto": True, "speed": 100, "lanes": 8},
+ {"name": "Ethernet3", "auto": True, "speed": 100},
+ {"name": "Ethernet4", "auto": False, "speed": 2.5},
+ ]
+ },
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure-incorrect-speed",
+ "test": VerifyInterfacesSpeed,
+ "eos_data": [
+ {
+ "interfaces": {
+ "Ethernet1": {
+ "bandwidth": 100000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 2,
+ },
+ "Ethernet1/1/1": {
+ "bandwidth": 100000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 2,
+ },
+ "Ethernet3": {
+ "bandwidth": 10000000000,
+ "autoNegotiate": "success",
+ "duplex": "duplexFull",
+ "lanes": 8,
+ },
+ "Ethernet4": {
+ "bandwidth": 25000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 8,
+ },
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Ethernet1", "auto": False, "speed": 1},
+ {"name": "Ethernet1/1/1", "auto": False, "speed": 1},
+ {"name": "Ethernet3", "auto": True, "speed": 100},
+ {"name": "Ethernet4", "auto": False, "speed": 2.5},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "For interface Ethernet1:\nExpected `1Gbps` as the speed, but found `100Gbps` instead.",
+ "For interface Ethernet1/1/1:\nExpected `1Gbps` as the speed, but found `100Gbps` instead.",
+ "For interface Ethernet3:\nExpected `100Gbps` as the speed, but found `10Gbps` instead.",
+ "For interface Ethernet4:\nExpected `2.5Gbps` as the speed, but found `25Gbps` instead.",
+ ],
+ },
+ },
+ {
+ "name": "failure-incorrect-mode",
+ "test": VerifyInterfacesSpeed,
+ "eos_data": [
+ {
+ "interfaces": {
+ "Ethernet1": {
+ "bandwidth": 1000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexHalf",
+ "lanes": 2,
+ },
+ "Ethernet1/2/2": {
+ "bandwidth": 1000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexHalf",
+ "lanes": 2,
+ },
+ "Ethernet3": {
+ "bandwidth": 100000000000,
+ "autoNegotiate": "success",
+ "duplex": "duplexHalf",
+ "lanes": 8,
+ },
+ "Ethernet4": {
+ "bandwidth": 2500000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexHalf",
+ "lanes": 8,
+ },
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Ethernet1", "auto": False, "speed": 1},
+ {"name": "Ethernet1/2/2", "auto": False, "speed": 1},
+ {"name": "Ethernet3", "auto": True, "speed": 100},
+ {"name": "Ethernet3", "auto": True, "speed": 100, "lanes": 8},
+ {"name": "Ethernet4", "auto": False, "speed": 2.5},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "For interface Ethernet1:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.",
+ "For interface Ethernet1/2/2:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.",
+ "For interface Ethernet3:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.",
+ "For interface Ethernet3:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.",
+ "For interface Ethernet4:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.",
+ ],
+ },
+ },
+ {
+ "name": "failure-incorrect-lane",
+ "test": VerifyInterfacesSpeed,
+ "eos_data": [
+ {
+ "interfaces": {
+ "Ethernet1": {
+ "bandwidth": 1000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 4,
+ },
+ "Ethernet2": {
+ "bandwidth": 10000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 4,
+ },
+ "Ethernet3": {
+ "bandwidth": 100000000000,
+ "autoNegotiate": "success",
+ "duplex": "duplexFull",
+ "lanes": 4,
+ },
+ "Ethernet4": {
+ "bandwidth": 2500000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 6,
+ },
+ "Ethernet4/1/1": {
+ "bandwidth": 2500000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexFull",
+ "lanes": 6,
+ },
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Ethernet1", "auto": False, "speed": 1, "lanes": 2},
+ {"name": "Ethernet3", "auto": True, "speed": 100, "lanes": 8},
+ {"name": "Ethernet4", "auto": False, "speed": 2.5, "lanes": 4},
+ {"name": "Ethernet4/1/1", "auto": False, "speed": 2.5, "lanes": 4},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "For interface Ethernet1:\nExpected `2` as the lanes, but found `4` instead.",
+ "For interface Ethernet3:\nExpected `8` as the lanes, but found `4` instead.",
+ "For interface Ethernet4:\nExpected `4` as the lanes, but found `6` instead.",
+ "For interface Ethernet4/1/1:\nExpected `4` as the lanes, but found `6` instead.",
+ ],
+ },
+ },
+ {
+ "name": "failure-all-type",
+ "test": VerifyInterfacesSpeed,
+ "eos_data": [
+ {
+ "interfaces": {
+ "Ethernet1": {
+ "bandwidth": 10000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexHalf",
+ "lanes": 4,
+ },
+ "Ethernet2/1/2": {
+ "bandwidth": 1000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexHalf",
+ "lanes": 2,
+ },
+ "Ethernet3": {
+ "bandwidth": 10000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexHalf",
+ "lanes": 6,
+ },
+ "Ethernet4": {
+ "bandwidth": 25000000000,
+ "autoNegotiate": "unknown",
+ "duplex": "duplexHalf",
+ "lanes": 4,
+ },
+ }
+ }
+ ],
+ "inputs": {
+ "interfaces": [
+ {"name": "Ethernet1", "auto": False, "speed": 1},
+ {"name": "Ethernet1", "auto": False, "speed": 1, "lanes": 2},
+ {"name": "Ethernet2/1/2", "auto": False, "speed": 10},
+ {"name": "Ethernet3", "auto": True, "speed": 1},
+ {"name": "Ethernet3", "auto": True, "speed": 100, "lanes": 8},
+ {"name": "Ethernet3", "auto": True, "speed": 100},
+ {"name": "Ethernet4", "auto": False, "speed": 2.5},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "For interface Ethernet1:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.\n"
+ "Expected `1Gbps` as the speed, but found `10Gbps` instead.",
+ "For interface Ethernet1:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.\n"
+ "Expected `1Gbps` as the speed, but found `10Gbps` instead.\n"
+ "Expected `2` as the lanes, but found `4` instead.",
+ "For interface Ethernet2/1/2:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.\n"
+ "Expected `10Gbps` as the speed, but found `1Gbps` instead.",
+ "For interface Ethernet3:\nExpected `success` as the auto negotiation, but found `unknown` instead.\n"
+ "Expected `duplexFull` as the duplex mode, but found `duplexHalf` instead.",
+ "For interface Ethernet3:\nExpected `success` as the auto negotiation, but found `unknown` instead.\n"
+ "Expected `duplexFull` as the duplex mode, but found `duplexHalf` instead.\n"
+ "Expected `100Gbps` as the speed, but found `10Gbps` instead.\n"
+ "Expected `8` as the lanes, but found `6` instead.",
+ "For interface Ethernet3:\nExpected `success` as the auto negotiation, but found `unknown` instead.\n"
+ "Expected `duplexFull` as the duplex mode, but found `duplexHalf` instead.\n"
+ "Expected `100Gbps` as the speed, but found `10Gbps` instead.",
+ "For interface Ethernet4:\nExpected `duplexFull` as the duplex mode, but found `duplexHalf` instead.\n"
+ "Expected `2.5Gbps` as the speed, but found `25Gbps` instead.",
+ ],
+ },
+ },
]
diff --git a/tests/units/anta_tests/test_logging.py b/tests/units/anta_tests/test_logging.py
index 1e8ee3d..d46c865 100644
--- a/tests/units/anta_tests/test_logging.py
+++ b/tests/units/anta_tests/test_logging.py
@@ -206,7 +206,9 @@ DATA: list[dict[str, Any]] = [
"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",
+ "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"},
@@ -223,6 +225,16 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "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_INFO: Message from arista on command-api (10.22.1.107): BLAH\n",
+ ],
+ "inputs": None,
+ "expected": {"result": "failure", "messages": ["Logs are not generated with the appropriate timestamp format"]},
+ },
+ {
"name": "success",
"test": VerifyLoggingAccounting,
"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"],
diff --git a/tests/units/anta_tests/test_path_selection.py b/tests/units/anta_tests/test_path_selection.py
new file mode 100644
index 0000000..c5fb079
--- /dev/null
+++ b/tests/units/anta_tests/test_path_selection.py
@@ -0,0 +1,327 @@
+# 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.
+"""Tests for anta.tests.path_selection.py."""
+
+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
+
+DATA: list[dict[str, Any]] = [
+ {
+ "name": "success",
+ "test": VerifyPathsHealth,
+ "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}}},
+ },
+ },
+ },
+ },
+ "10.255.0.2": {
+ "dpsGroups": {
+ "internet": {
+ "dpsPaths": {
+ "path1": {"state": "ipsecEstablished", "dpsSessions": {"0": {"active": True}}},
+ },
+ },
+ "mpls": {
+ "dpsPaths": {
+ "path2": {"state": "routeResolved", "dpsSessions": {"0": {"active": True}}},
+ },
+ },
+ },
+ },
+ }
+ },
+ ],
+ "inputs": {},
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure-no-peer",
+ "test": VerifyPathsHealth,
+ "eos_data": [
+ {"dpsPeers": {}},
+ ],
+ "inputs": {},
+ "expected": {"result": "failure", "messages": ["No path configured for router path-selection."]},
+ },
+ {
+ "name": "failure-not-established",
+ "test": VerifyPathsHealth,
+ "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}}},
+ },
+ },
+ },
+ },
+ "10.255.0.2": {
+ "dpsGroups": {
+ "internet": {
+ "dpsPaths": {
+ "path1": {"state": "ipsecEstablished", "dpsSessions": {"0": {"active": True}}},
+ },
+ },
+ "mpls": {
+ "dpsPaths": {
+ "path2": {"state": "ipsecPending", "dpsSessions": {"0": {"active": False}}},
+ },
+ },
+ },
+ },
+ }
+ },
+ ],
+ "inputs": {},
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "Path state for peer 10.255.0.1 in path-group internet is `ipsecPending`.",
+ "Path state for peer 10.255.0.1 in path-group mpls is `ipsecPending`.",
+ "Path state for peer 10.255.0.2 in path-group mpls is `ipsecPending`.",
+ ],
+ },
+ },
+ {
+ "name": "failure-inactive",
+ "test": VerifyPathsHealth,
+ "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}}},
+ },
+ },
+ },
+ },
+ "10.255.0.2": {
+ "dpsGroups": {
+ "internet": {
+ "dpsPaths": {
+ "path1": {"state": "routeResolved", "dpsSessions": {"0": {"active": True}}},
+ },
+ },
+ "mpls": {
+ "dpsPaths": {
+ "path2": {"state": "routeResolved", "dpsSessions": {"0": {"active": False}}},
+ },
+ },
+ },
+ },
+ }
+ },
+ ],
+ "inputs": {},
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "Telemetry state for peer 10.255.0.1 in path-group internet is `inactive`.",
+ "Telemetry state for peer 10.255.0.1 in path-group mpls is `inactive`.",
+ "Telemetry state for peer 10.255.0.2 in path-group mpls is `inactive`.",
+ ],
+ },
+ },
+ {
+ "name": "success",
+ "test": VerifySpecificPath,
+ "eos_data": [
+ {
+ "dpsPeers": {
+ "10.255.0.1": {
+ "dpsGroups": {
+ "internet": {
+ "dpsPaths": {
+ "path3": {
+ "state": "ipsecEstablished",
+ "source": "172.18.13.2",
+ "destination": "172.18.15.2",
+ "dpsSessions": {"0": {"active": True}},
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "dpsPeers": {
+ "10.255.0.2": {
+ "dpsGroups": {
+ "mpls": {
+ "dpsPaths": {
+ "path2": {
+ "state": "ipsecEstablished",
+ "source": "172.18.3.2",
+ "destination": "172.18.5.2",
+ "dpsSessions": {"0": {"active": True}},
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": {
+ "paths": [
+ {"peer": "10.255.0.1", "path_group": "internet", "source_address": "172.18.3.2", "destination_address": "172.18.5.2"},
+ {"peer": "10.255.0.2", "path_group": "mpls", "source_address": "172.18.13.2", "destination_address": "172.18.15.2"},
+ ]
+ },
+ "expected": {"result": "success"},
+ },
+ {
+ "name": "failure-no-peer",
+ "test": VerifySpecificPath,
+ "eos_data": [
+ {"dpsPeers": {}},
+ {"dpsPeers": {}},
+ ],
+ "inputs": {
+ "paths": [
+ {"peer": "10.255.0.1", "path_group": "internet", "source_address": "172.18.3.2", "destination_address": "172.18.5.2"},
+ {"peer": "10.255.0.2", "path_group": "mpls", "source_address": "172.18.13.2", "destination_address": "172.18.15.2"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "Path `peer: 10.255.0.1 source: 172.18.3.2 destination: 172.18.5.2` is not configured for path-group `internet`.",
+ "Path `peer: 10.255.0.2 source: 172.18.13.2 destination: 172.18.15.2` is not configured for path-group `mpls`.",
+ ],
+ },
+ },
+ {
+ "name": "failure-not-established",
+ "test": VerifySpecificPath,
+ "eos_data": [
+ {
+ "dpsPeers": {
+ "10.255.0.1": {
+ "dpsGroups": {
+ "internet": {
+ "dpsPaths": {
+ "path3": {"state": "ipsecPending", "source": "172.18.3.2", "destination": "172.18.5.2", "dpsSessions": {"0": {"active": True}}}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "dpsPeers": {
+ "10.255.0.2": {
+ "dpsGroups": {
+ "mpls": {
+ "dpsPaths": {
+ "path4": {
+ "state": "ipsecPending",
+ "source": "172.18.13.2",
+ "destination": "172.18.15.2",
+ "dpsSessions": {"0": {"active": False}},
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": {
+ "paths": [
+ {"peer": "10.255.0.1", "path_group": "internet", "source_address": "172.18.3.2", "destination_address": "172.18.5.2"},
+ {"peer": "10.255.0.2", "path_group": "mpls", "source_address": "172.18.13.2", "destination_address": "172.18.15.2"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "Path state for `peer: 10.255.0.1 source: 172.18.3.2 destination: 172.18.5.2` in path-group internet is `ipsecPending`.",
+ "Path state for `peer: 10.255.0.2 source: 172.18.13.2 destination: 172.18.15.2` in path-group mpls is `ipsecPending`.",
+ ],
+ },
+ },
+ {
+ "name": "failure-inactive",
+ "test": VerifySpecificPath,
+ "eos_data": [
+ {
+ "dpsPeers": {
+ "10.255.0.1": {
+ "dpsGroups": {
+ "internet": {
+ "dpsPaths": {
+ "path3": {"state": "routeResolved", "source": "172.18.3.2", "destination": "172.18.5.2", "dpsSessions": {"0": {"active": False}}}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "dpsPeers": {
+ "10.255.0.2": {
+ "dpsGroups": {
+ "mpls": {
+ "dpsPaths": {
+ "path4": {
+ "state": "routeResolved",
+ "source": "172.18.13.2",
+ "destination": "172.18.15.2",
+ "dpsSessions": {"0": {"active": False}},
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ ],
+ "inputs": {
+ "paths": [
+ {"peer": "10.255.0.1", "path_group": "internet", "source_address": "172.18.3.2", "destination_address": "172.18.5.2"},
+ {"peer": "10.255.0.2", "path_group": "mpls", "source_address": "172.18.13.2", "destination_address": "172.18.15.2"},
+ ]
+ },
+ "expected": {
+ "result": "failure",
+ "messages": [
+ "Telemetry state for path `peer: 10.255.0.1 source: 172.18.3.2 destination: 172.18.5.2` in path-group internet is `inactive`.",
+ "Telemetry state for path `peer: 10.255.0.2 source: 172.18.13.2 destination: 172.18.15.2` in path-group mpls is `inactive`.",
+ ],
+ },
+ },
+]
diff --git a/tests/units/anta_tests/test_ptp.py b/tests/units/anta_tests/test_ptp.py
index ef42a58..8f4c77f 100644
--- a/tests/units/anta_tests/test_ptp.py
+++ b/tests/units/anta_tests/test_ptp.py
@@ -42,11 +42,11 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "failure", "messages": ["The device is not configured as a PTP Boundary Clock: 'ptpDisabled'"]},
},
{
- "name": "error",
+ "name": "skipped",
"test": VerifyPtpModeStatus,
"eos_data": [{"ptpIntfSummaries": {}}],
"inputs": None,
- "expected": {"result": "error", "messages": ["'ptpMode' variable is not present in the command output"]},
+ "expected": {"result": "skipped", "messages": ["PTP is not configured"]},
},
{
"name": "success",
@@ -104,11 +104,11 @@ DATA: list[dict[str, Any]] = [
},
},
{
- "name": "error",
+ "name": "skipped",
"test": VerifyPtpGMStatus,
"eos_data": [{"ptpIntfSummaries": {}}],
"inputs": {"gmid": "0xec:46:70:ff:fe:00:ff:a8"},
- "expected": {"result": "error", "messages": ["'ptpClockSummary' variable is not present in the command output"]},
+ "expected": {"result": "skipped", "messages": ["PTP is not configured"]},
},
{
"name": "success",
@@ -161,14 +161,14 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "failure", "messages": ["The device lock is more than 60s old: 157s"]},
},
{
- "name": "error",
+ "name": "skipped",
"test": VerifyPtpLockStatus,
"eos_data": [{"ptpIntfSummaries": {}}],
"inputs": None,
"expected": {
- "result": "error",
+ "result": "skipped",
"messages": [
- "'ptpClockSummary' variable is not present in the command output",
+ "PTP is not configured",
],
},
},
diff --git a/tests/units/anta_tests/test_security.py b/tests/units/anta_tests/test_security.py
index 4c28541..3a732bd 100644
--- a/tests/units/anta_tests/test_security.py
+++ b/tests/units/anta_tests/test_security.py
@@ -34,7 +34,14 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "success"},
},
{
- "name": "failure",
+ "name": "error-missing-ssh-status",
+ "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."]},
+ },
+ {
+ "name": "failure-ssh-disabled",
"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,
@@ -574,6 +581,40 @@ 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,
"eos_data": [
diff --git a/tests/units/anta_tests/test_services.py b/tests/units/anta_tests/test_services.py
index ed86e10..61c44d0 100644
--- a/tests/units/anta_tests/test_services.py
+++ b/tests/units/anta_tests/test_services.py
@@ -127,10 +127,12 @@ DATA: list[dict[str, Any]] = [
"name": "success",
"test": VerifyErrdisableRecovery,
"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
"""
diff --git a/tests/units/anta_tests/test_software.py b/tests/units/anta_tests/test_software.py
index 84e90e8..e46f526 100644
--- a/tests/units/anta_tests/test_software.py
+++ b/tests/units/anta_tests/test_software.py
@@ -79,6 +79,7 @@ DATA: list[dict[str, Any]] = [
"inputs": {"versions": ["v1.17.1", "v1.18.1"]},
"expected": {"result": "failure", "messages": ["device is running TerminAttr version v1.17.0 and is not in the allowed list: ['v1.17.1', 'v1.18.1']"]},
},
+ # TODO: add a test with a real extension?
{
"name": "success-no-extensions",
"test": VerifyEOSExtensions,
@@ -90,6 +91,16 @@ DATA: list[dict[str, Any]] = [
"expected": {"result": "success"},
},
{
+ "name": "success-empty-extension",
+ "test": VerifyEOSExtensions,
+ "eos_data": [
+ {"extensions": {}, "extensionStoredDir": "flash:", "warnings": ["No extensions are available"]},
+ {"extensions": [""]},
+ ],
+ "inputs": None,
+ "expected": {"result": "success"},
+ },
+ {
"name": "failure",
"test": VerifyEOSExtensions,
"eos_data": [