From 3056c03aab8133bf0aac386822e3d23cd64b8ff0 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 14 Nov 2024 05:56:01 +0000 Subject: [PATCH 01/10] Issue_884: Added testcases for verifying Route Type --- anta/custom_types.py | 14 +- anta/tests/routing/generic.py | 107 ++++- .../units/anta_tests/routing/test_generic.py | 452 +++++++++++++++++- 3 files changed, 563 insertions(+), 10 deletions(-) diff --git a/anta/custom_types.py b/anta/custom_types.py index c29811826..1a6913982 100644 --- a/anta/custom_types.py +++ b/anta/custom_types.py @@ -202,9 +202,19 @@ def validate_regex(value: str) -> str: "prefixRtMembershipDroppedLocalAsReject", "prefixRtMembershipDroppedMaxRouteLimitViolated", ] -BgpUpdateError = Literal["inUpdErrWithdraw", "inUpdErrIgnore", "inUpdErrDisableAfiSafi", "disabledAfiSafi", "lastUpdErrTime"] -BfdProtocol = Literal["bgp", "isis", "lag", "ospf", "ospfv3", "pim", "route-input", "static-bfd", "static-route", "vrrp", "vxlan"] +BgpUpdateError = Literal[ + "inUpdErrWithdraw", "inUpdErrIgnore", "inUpdErrDisableAfiSafi", "disabledAfiSafi", "lastUpdErrTime"] +BfdProtocol = Literal[ + "bgp", "isis", "lag", "ospf", "ospfv3", "pim", "route-input", "static-bfd", "static-route", "vrrp", "vxlan"] SnmpPdu = Literal["inGetPdus", "inGetNextPdus", "inSetPdus", "outGetResponsePdus", "outTrapPdus"] SnmpErrorCounter = Literal[ "inVersionErrs", "inBadCommunityNames", "inBadCommunityUses", "inParseErrs", "outTooBigErrs", "outNoSuchNameErrs", "outBadValueErrs", "outGeneralErrs" ] +# TODO: Needs to update the route types with confirmation. + +RouteType = Literal["connected", "static", "kernel", "OSPF", "OSPF inter area", "OSPF external type 1", + "OSPF external type 2", "OSPF NSSA external type 1", "OSPF NSSA external type2", "Other BGP Routes", + "iBGP", "eBGP", "RIP", "IS-IS level 1", "IS-IS level 2", "OSPFv3", "BGP Aggregate", + "OSPF Summary", "Nexthop Group Static Route", "VXLAN Control Service", "Martian", + "DHCP client installed default route","Dynamic Policy Route", "VRF Leaked", "gRIBI", + "Route Cache Route", "CBF Leaked Route"] diff --git a/anta/tests/routing/generic.py b/anta/tests/routing/generic.py index fb9e3175d..321b7cbc1 100644 --- a/anta/tests/routing/generic.py +++ b/anta/tests/routing/generic.py @@ -8,13 +8,14 @@ from __future__ import annotations from functools import cache -from ipaddress import IPv4Address, IPv4Interface -from typing import TYPE_CHECKING, ClassVar, Literal +from ipaddress import IPv4Address, IPv4Interface, IPv4Network +from typing import TYPE_CHECKING, ClassVar, Literal, Any -from pydantic import model_validator +from pydantic import model_validator, BaseModel -from anta.custom_types import PositiveInteger +from anta.custom_types import PositiveInteger, RouteType from anta.models import AntaCommand, AntaTemplate, AntaTest +from anta.tools import get_value if TYPE_CHECKING: import sys @@ -61,7 +62,8 @@ def test(self) -> None: if configured_model == operating_model == self.inputs.model: self.result.is_success() else: - self.result.is_failure(f"routing model is misconfigured: configured: {configured_model} - operating: {operating_model} - expected: {self.inputs.model}") + self.result.is_failure( + f"routing model is misconfigured: configured: {configured_model} - operating: {operating_model} - expected: {self.inputs.model}") class VerifyRoutingTableSize(AntaTest): @@ -110,7 +112,8 @@ def test(self) -> None: if self.inputs.minimum <= total_routes <= self.inputs.maximum: self.result.is_success() else: - self.result.is_failure(f"routing-table has {total_routes} routes and not between min ({self.inputs.minimum}) and maximum ({self.inputs.maximum})") + self.result.is_failure( + f"routing-table has {total_routes} routes and not between min ({self.inputs.minimum}) and maximum ({self.inputs.maximum})") class VerifyRoutingTableEntry(AntaTest): @@ -180,4 +183,94 @@ def test(self) -> None: if not missing_routes: self.result.is_success() else: - self.result.is_failure(f"The following route(s) are missing from the routing table of VRF {self.inputs.vrf}: {missing_routes}") + self.result.is_failure( + f"The following route(s) are missing from the routing table of VRF {self.inputs.vrf}: {missing_routes}") + + +class VerifyRouteType(AntaTest): + """Verifies the type of the provided route in the routing table of a specified VRF. + + Expected Results + ---------------- + * Success: The test will pass if the provided route is of the expected type. + * Failure: The test will fail if the provided route is not of the expected type. + Examples + -------- + ```yaml + anta.tests.routing: + generic: + VerifyRouteType: + routes_entries: + - vrf: default + prefix: 10.10.0.1/32 + route_type: eBGP + - vrf: default + prefix: 10.100.0.12/31 + route_type: connected + - vrf: default + prefix: 10.100.0.14/31 + route_type: connected + - vrf: default + prefix: 10.100.0.128/31 + route_type: eBGP + - vrf: default + prefix: 10.100.1.5/32 + route_type: iBGP``` + """ + categories: ClassVar[list[str]] = ["routing"] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [ + AntaCommand(command="show ip route vrf all", revision=4), + ] + + class Input(AntaTest.Input): + """ Input model for the VerifyRouteType test. """ + routes_entries: list[Routes] + + class Routes(BaseModel): + """ Model for a list of route entries.""" + + vrf: str = "default" + """ VRF context. Defaults to `default` VRF.""" + prefix: IPv4Network + """ IPV4network to validate the rout type. """ + route_type: RouteType + """ List of Route type to validate the valid rout type. """ + @AntaTest.anta_test + def test(self) -> None: + """Main test function for VerifyRouteType.""" + self.result.is_success() + + # Forming a dictionary for the test failure message. + failures: dict[str, any] = {"routes_entries": {}} + + # Collecting the 'show ip route vrf all' command output. + output = self.instance_commands[0].json_output + + # Iterating over the all routes entries mentioned in the inputs. + for entries in self.inputs.routes_entries: + vrf = entries.vrf + network = str(entries.prefix) + expected_route_type = entries.route_type + + # Verifying that on device, expected VRF is configured. + if (routes_details := get_value(output, f"vrfs.{vrf}.routes")) is None: + failures["routes_entries"][network] = {vrf: "Not configured"} + continue + + # Verifying that the expected route is present or not on the device + if (route_data := get_value(routes_details, network, separator="..")) is None: + failures["routes_entries"][network] = {vrf: "Routes not found."} + continue + + actual_route_type = route_data.get("routeType") + + # Verifying that the expected route-type and the actual routes are the same. + if expected_route_type != actual_route_type: + failures["routes_entries"][network] = {vrf: { + "route_type": f"Expected route type is '{expected_route_type}' " + f"however in actual it is found as '{actual_route_type}'"}} + + # Updating the result, as per the testcase failure message. + if failures["routes_entries"]: + self.result.is_failure( + f"For following routes, VRF is not configured or Route types are invalid:\n{failures}") diff --git a/tests/units/anta_tests/routing/test_generic.py b/tests/units/anta_tests/routing/test_generic.py index 20f83b92b..95f3159ec 100644 --- a/tests/units/anta_tests/routing/test_generic.py +++ b/tests/units/anta_tests/routing/test_generic.py @@ -11,7 +11,7 @@ import pytest from pydantic import ValidationError -from anta.tests.routing.generic import VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize +from anta.tests.routing.generic import VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize, VerifyRouteType from tests.units.anta_tests import test DATA: list[dict[str, Any]] = [ @@ -304,9 +304,459 @@ "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']"]}, }, + + +{ + "name": "Failure-route-not-found", + "test": VerifyRouteType, + "eos_data": [ +{ + "vrfs": { + "default": { + "routingDisabled": False, + "allRoutesProgrammedHardware": True, + "allRoutesProgrammedKernel": True, + "defaultRouteState": "notSet", + "routes": { + "10.10.0.1/32": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.0.12", + "interface": "Ethernet1" + }, + { + "nexthopAddr": "10.100.0.14", + "interface": "Ethernet2" + } + ], + "directlyConnected": False + }, + "10.100.0.12/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [ + { + "interface": "Ethernet1" + } + ], + "directlyConnected": True + }, + "10.100.0.14/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [ + { + "interface": "Ethernet2" + } + ], + "directlyConnected": True + }, + "10.100.0.128/31": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.0.12", + "interface": "Ethernet1" + }, + { + "nexthopAddr": "10.100.0.14", + "interface": "Ethernet2" + } + ], + "directlyConnected": False + }, + "10.100.1.5/32": { + "hardwareProgrammed": True, + "routeType": "iBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "directlyConnected": False, + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.4.4", + "interface": "Vlan4093" + } + ] + },} + +}}} + ], + + "inputs": + {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, {"vrf": "default", "prefix": "10.100.0.12/32", "route_type": "connected"}] + }, + "expected": {"result": "failure", "messages": [ + "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {" + "'10.100.0.12/32': {'default': 'Routes not found.'}}}"]}, + }, + { + "name": "Success-valid-route-type", + "test": VerifyRouteType, + "eos_data": [ + { + "vrfs": { + "default": { + "routingDisabled": False, + "allRoutesProgrammedHardware": True, + "allRoutesProgrammedKernel": True, + "defaultRouteState": "notSet", + "routes": { + "10.10.0.1/32": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.0.12", + "interface": "Ethernet1" + }, + { + "nexthopAddr": "10.100.0.14", + "interface": "Ethernet2" + } + ], + "directlyConnected": False + }, + "10.100.0.12/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [ + { + "interface": "Ethernet1" + } + ], + "directlyConnected": True + }, + "10.100.0.14/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [ + { + "interface": "Ethernet2" + } + ], + "directlyConnected": True + }, + "10.100.0.128/31": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.0.12", + "interface": "Ethernet1" + }, + { + "nexthopAddr": "10.100.0.14", + "interface": "Ethernet2" + } + ], + "directlyConnected": False + }, + "10.100.1.5/32": { + "hardwareProgrammed": True, + "routeType": "iBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "directlyConnected": False, + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.4.4", + "interface": "Vlan4093" + } + ] + }, } + + }}} + ], + + "inputs": {"routes_entries": [ + {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, + ]}, + "expected": {"result": "success"}, + }, +{ + "name": "Failure-invalid-route-type", + "test": VerifyRouteType, + "eos_data": [ + { + "vrfs": { + "default": { + "routingDisabled": False, + "allRoutesProgrammedHardware": True, + "allRoutesProgrammedKernel": True, + "defaultRouteState": "notSet", + "routes": { + "10.10.0.1/32": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.0.12", + "interface": "Ethernet1" + }, + { + "nexthopAddr": "10.100.0.14", + "interface": "Ethernet2" + } + ], + "directlyConnected": False + }, + "10.100.0.12/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [ + { + "interface": "Ethernet1" + } + ], + "directlyConnected": True + }, + "10.100.0.14/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [ + { + "interface": "Ethernet2" + } + ], + "directlyConnected": True + }, + "10.100.0.128/31": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.0.12", + "interface": "Ethernet1" + }, + { + "nexthopAddr": "10.100.0.14", + "interface": "Ethernet2" + } + ], + "directlyConnected": False + }, + "10.100.1.5/32": { + "hardwareProgrammed": True, + "routeType": "iBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "directlyConnected": False, + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.4.4", + "interface": "Vlan4093" + } + ] + }, } + + }}} + ], + + "inputs": {"routes_entries": [ + {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "iBGP"}, + {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "static"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "eBGP"}, + ]}, + "expected": {"result": "failure", "messages": ['For following routes, VRF is not configured or Route types are invalid:\n{\'routes_entries\': {\'10.10.0.1/32\': {\'default\': {\'route_type\': "Expected route type is \'iBGP\' however in actual it is found as \'eBGP\'"}}, \'10.100.0.14/31\': {\'default\': {\'route_type\': "Expected route type is \'static\' however in actual it is found as \'connected\'"}}, \'10.100.1.5/32\': {\'default\': {\'route_type\': "Expected route type is \'eBGP\' however in actual it is found as \'iBGP\'"}}}}']}, + }, +{ + "name": "Failure-vrf-not-configured", + "test": VerifyRouteType, + "eos_data": [ + { + "vrfs": {} + } + ], + + "inputs": {"routes_entries": [ + {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, + ]}, + "expected": {"result": "failure", "messages": ["For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {'10.10.0.1/32': {'default': 'Not configured'}, '10.100.0.12/31': {'default': 'Not configured'}, '10.100.0.14/31': {'default': 'Not configured'}, '10.100.0.128/31': {'default': 'Not configured'}, '10.100.1.5/32': {'default': 'Not configured'}}}"]}, + }, +{ + "name": "Failure-invalid-network-address", + "test": VerifyRouteType, + "eos_data": [ + { + "vrfs": { + "default": { + "routingDisabled": False, + "allRoutesProgrammedHardware": True, + "allRoutesProgrammedKernel": True, + "defaultRouteState": "notSet", + "routes": { + "10.10.0.1/32": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.0.12", + "interface": "Ethernet1" + }, + { + "nexthopAddr": "10.100.0.14", + "interface": "Ethernet2" + } + ], + "directlyConnected": False + }, + "10.100.0.12/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [ + { + "interface": "Ethernet1" + } + ], + "directlyConnected": True + }, + "10.100.0.14/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [ + { + "interface": "Ethernet2" + } + ], + "directlyConnected": True + }, + "10.100.0.128/31": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.0.12", + "interface": "Ethernet1" + }, + { + "nexthopAddr": "10.100.0.14", + "interface": "Ethernet2" + } + ], + "directlyConnected": False + }, + "10.100.1.5/32": { + "hardwareProgrammed": True, + "routeType": "iBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "directlyConnected": False, + "preference": 200, + "metric": 0, + "vias": [ + { + "nexthopAddr": "10.100.4.4", + "interface": "Vlan4093" + } + ] + }, } + + }}} + ], + + "inputs": {"routes_entries": [ + {"vrf": "default", "prefix": "1022.10.0.1/32", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "2001:db8:3333:4444:5555:6666:7777:8888:", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, + ]}, + "expected": {"result": "error", "messages": ["Input is not a valid IPv4 network"]}, }, + + ] + + + class TestVerifyRoutingTableSizeInputs: """Test anta.tests.routing.generic.VerifyRoutingTableSize.Input.""" From 5d261f90493f467b99e0d2fce2ea9ea1e082d32b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 06:23:51 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- anta/custom_types.py | 41 +- anta/tests/routing/generic.py | 30 +- .../units/anta_tests/routing/test_generic.py | 452 +++++++----------- 3 files changed, 219 insertions(+), 304 deletions(-) diff --git a/anta/custom_types.py b/anta/custom_types.py index 1a6913982..a6972a727 100644 --- a/anta/custom_types.py +++ b/anta/custom_types.py @@ -202,19 +202,40 @@ def validate_regex(value: str) -> str: "prefixRtMembershipDroppedLocalAsReject", "prefixRtMembershipDroppedMaxRouteLimitViolated", ] -BgpUpdateError = Literal[ - "inUpdErrWithdraw", "inUpdErrIgnore", "inUpdErrDisableAfiSafi", "disabledAfiSafi", "lastUpdErrTime"] -BfdProtocol = Literal[ - "bgp", "isis", "lag", "ospf", "ospfv3", "pim", "route-input", "static-bfd", "static-route", "vrrp", "vxlan"] +BgpUpdateError = Literal["inUpdErrWithdraw", "inUpdErrIgnore", "inUpdErrDisableAfiSafi", "disabledAfiSafi", "lastUpdErrTime"] +BfdProtocol = Literal["bgp", "isis", "lag", "ospf", "ospfv3", "pim", "route-input", "static-bfd", "static-route", "vrrp", "vxlan"] SnmpPdu = Literal["inGetPdus", "inGetNextPdus", "inSetPdus", "outGetResponsePdus", "outTrapPdus"] SnmpErrorCounter = Literal[ "inVersionErrs", "inBadCommunityNames", "inBadCommunityUses", "inParseErrs", "outTooBigErrs", "outNoSuchNameErrs", "outBadValueErrs", "outGeneralErrs" ] # TODO: Needs to update the route types with confirmation. -RouteType = Literal["connected", "static", "kernel", "OSPF", "OSPF inter area", "OSPF external type 1", - "OSPF external type 2", "OSPF NSSA external type 1", "OSPF NSSA external type2", "Other BGP Routes", - "iBGP", "eBGP", "RIP", "IS-IS level 1", "IS-IS level 2", "OSPFv3", "BGP Aggregate", - "OSPF Summary", "Nexthop Group Static Route", "VXLAN Control Service", "Martian", - "DHCP client installed default route","Dynamic Policy Route", "VRF Leaked", "gRIBI", - "Route Cache Route", "CBF Leaked Route"] +RouteType = Literal[ + "connected", + "static", + "kernel", + "OSPF", + "OSPF inter area", + "OSPF external type 1", + "OSPF external type 2", + "OSPF NSSA external type 1", + "OSPF NSSA external type2", + "Other BGP Routes", + "iBGP", + "eBGP", + "RIP", + "IS-IS level 1", + "IS-IS level 2", + "OSPFv3", + "BGP Aggregate", + "OSPF Summary", + "Nexthop Group Static Route", + "VXLAN Control Service", + "Martian", + "DHCP client installed default route", + "Dynamic Policy Route", + "VRF Leaked", + "gRIBI", + "Route Cache Route", + "CBF Leaked Route", +] diff --git a/anta/tests/routing/generic.py b/anta/tests/routing/generic.py index 321b7cbc1..3cbd372dc 100644 --- a/anta/tests/routing/generic.py +++ b/anta/tests/routing/generic.py @@ -9,9 +9,9 @@ from functools import cache from ipaddress import IPv4Address, IPv4Interface, IPv4Network -from typing import TYPE_CHECKING, ClassVar, Literal, Any +from typing import TYPE_CHECKING, ClassVar, Literal -from pydantic import model_validator, BaseModel +from pydantic import BaseModel, model_validator from anta.custom_types import PositiveInteger, RouteType from anta.models import AntaCommand, AntaTemplate, AntaTest @@ -62,8 +62,7 @@ def test(self) -> None: if configured_model == operating_model == self.inputs.model: self.result.is_success() else: - self.result.is_failure( - f"routing model is misconfigured: configured: {configured_model} - operating: {operating_model} - expected: {self.inputs.model}") + self.result.is_failure(f"routing model is misconfigured: configured: {configured_model} - operating: {operating_model} - expected: {self.inputs.model}") class VerifyRoutingTableSize(AntaTest): @@ -112,8 +111,7 @@ def test(self) -> None: if self.inputs.minimum <= total_routes <= self.inputs.maximum: self.result.is_success() else: - self.result.is_failure( - f"routing-table has {total_routes} routes and not between min ({self.inputs.minimum}) and maximum ({self.inputs.maximum})") + self.result.is_failure(f"routing-table has {total_routes} routes and not between min ({self.inputs.minimum}) and maximum ({self.inputs.maximum})") class VerifyRoutingTableEntry(AntaTest): @@ -183,8 +181,7 @@ def test(self) -> None: if not missing_routes: self.result.is_success() else: - self.result.is_failure( - f"The following route(s) are missing from the routing table of VRF {self.inputs.vrf}: {missing_routes}") + self.result.is_failure(f"The following route(s) are missing from the routing table of VRF {self.inputs.vrf}: {missing_routes}") class VerifyRouteType(AntaTest): @@ -194,6 +191,7 @@ class VerifyRouteType(AntaTest): ---------------- * Success: The test will pass if the provided route is of the expected type. * Failure: The test will fail if the provided route is not of the expected type. + Examples -------- ```yaml @@ -217,17 +215,19 @@ class VerifyRouteType(AntaTest): prefix: 10.100.1.5/32 route_type: iBGP``` """ + categories: ClassVar[list[str]] = ["routing"] commands: ClassVar[list[AntaCommand | AntaTemplate]] = [ AntaCommand(command="show ip route vrf all", revision=4), ] class Input(AntaTest.Input): - """ Input model for the VerifyRouteType test. """ + """Input model for the VerifyRouteType test.""" + routes_entries: list[Routes] class Routes(BaseModel): - """ Model for a list of route entries.""" + """Model for a list of route entries.""" vrf: str = "default" """ VRF context. Defaults to `default` VRF.""" @@ -235,6 +235,7 @@ class Routes(BaseModel): """ IPV4network to validate the rout type. """ route_type: RouteType """ List of Route type to validate the valid rout type. """ + @AntaTest.anta_test def test(self) -> None: """Main test function for VerifyRouteType.""" @@ -266,11 +267,10 @@ def test(self) -> None: # Verifying that the expected route-type and the actual routes are the same. if expected_route_type != actual_route_type: - failures["routes_entries"][network] = {vrf: { - "route_type": f"Expected route type is '{expected_route_type}' " - f"however in actual it is found as '{actual_route_type}'"}} + failures["routes_entries"][network] = { + vrf: {"route_type": f"Expected route type is '{expected_route_type}' " f"however in actual it is found as '{actual_route_type}'"} + } # Updating the result, as per the testcase failure message. if failures["routes_entries"]: - self.result.is_failure( - f"For following routes, VRF is not configured or Route types are invalid:\n{failures}") + self.result.is_failure(f"For following routes, VRF is not configured or Route types are invalid:\n{failures}") diff --git a/tests/units/anta_tests/routing/test_generic.py b/tests/units/anta_tests/routing/test_generic.py index 95f3159ec..b4d692bea 100644 --- a/tests/units/anta_tests/routing/test_generic.py +++ b/tests/units/anta_tests/routing/test_generic.py @@ -11,7 +11,7 @@ import pytest from pydantic import ValidationError -from anta.tests.routing.generic import VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize, VerifyRouteType +from anta.tests.routing.generic import VerifyRouteType, VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize from tests.units.anta_tests import test DATA: list[dict[str, Any]] = [ @@ -304,112 +304,87 @@ "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']"]}, }, - - -{ + { "name": "Failure-route-not-found", "test": VerifyRouteType, "eos_data": [ -{ - "vrfs": { - "default": { - "routingDisabled": False, - "allRoutesProgrammedHardware": True, - "allRoutesProgrammedKernel": True, - "defaultRouteState": "notSet", - "routes": { - "10.10.0.1/32": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.0.12", - "interface": "Ethernet1" - }, - { - "nexthopAddr": "10.100.0.14", - "interface": "Ethernet2" - } - ], - "directlyConnected": False - }, - "10.100.0.12/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [ - { - "interface": "Ethernet1" - } - ], - "directlyConnected": True - }, - "10.100.0.14/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [ - { - "interface": "Ethernet2" - } - ], - "directlyConnected": True - }, - "10.100.0.128/31": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.0.12", - "interface": "Ethernet1" + { + "vrfs": { + "default": { + "routingDisabled": False, + "allRoutesProgrammedHardware": True, + "allRoutesProgrammedKernel": True, + "defaultRouteState": "notSet", + "routes": { + "10.10.0.1/32": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "directlyConnected": False, + }, + "10.100.0.12/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [{"interface": "Ethernet1"}], + "directlyConnected": True, + }, + "10.100.0.14/31": { + "hardwareProgrammed": True, + "routeType": "connected", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "vias": [{"interface": "Ethernet2"}], + "directlyConnected": True, + }, + "10.100.0.128/31": { + "hardwareProgrammed": True, + "routeType": "eBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "preference": 200, + "metric": 0, + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "directlyConnected": False, + }, + "10.100.1.5/32": { + "hardwareProgrammed": True, + "routeType": "iBGP", + "routeLeaked": False, + "kernelProgrammed": True, + "routeAction": "forward", + "directlyConnected": False, + "preference": 200, + "metric": 0, + "vias": [{"nexthopAddr": "10.100.4.4", "interface": "Vlan4093"}], + }, }, - { - "nexthopAddr": "10.100.0.14", - "interface": "Ethernet2" - } - ], - "directlyConnected": False - }, - "10.100.1.5/32": { - "hardwareProgrammed": True, - "routeType": "iBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "directlyConnected": False, - "preference": 200, - "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.4.4", - "interface": "Vlan4093" - } - ] - },} - -}}} + } + } + } ], - - "inputs": - {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, {"vrf": "default", "prefix": "10.100.0.12/32", "route_type": "connected"}] - }, - "expected": {"result": "failure", "messages": [ - "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {" - "'10.100.0.12/32': {'default': 'Routes not found.'}}}"]}, + "inputs": { + "routes_entries": [ + {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.0.12/32", "route_type": "connected"}, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {" + "'10.100.0.12/32': {'default': 'Routes not found.'}}}" + ], + }, }, { "name": "Success-valid-route-type", @@ -431,17 +406,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.0.12", - "interface": "Ethernet1" - }, - { - "nexthopAddr": "10.100.0.14", - "interface": "Ethernet2" - } - ], - "directlyConnected": False + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "directlyConnected": False, }, "10.100.0.12/31": { "hardwareProgrammed": True, @@ -449,12 +415,8 @@ "routeLeaked": False, "kernelProgrammed": True, "routeAction": "forward", - "vias": [ - { - "interface": "Ethernet1" - } - ], - "directlyConnected": True + "vias": [{"interface": "Ethernet1"}], + "directlyConnected": True, }, "10.100.0.14/31": { "hardwareProgrammed": True, @@ -462,12 +424,8 @@ "routeLeaked": False, "kernelProgrammed": True, "routeAction": "forward", - "vias": [ - { - "interface": "Ethernet2" - } - ], - "directlyConnected": True + "vias": [{"interface": "Ethernet2"}], + "directlyConnected": True, }, "10.100.0.128/31": { "hardwareProgrammed": True, @@ -477,17 +435,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.0.12", - "interface": "Ethernet1" - }, - { - "nexthopAddr": "10.100.0.14", - "interface": "Ethernet2" - } - ], - "directlyConnected": False + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "directlyConnected": False, }, "10.100.1.5/32": { "hardwareProgrammed": True, @@ -498,27 +447,25 @@ "directlyConnected": False, "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.4.4", - "interface": "Vlan4093" - } - ] - }, } - - }}} + "vias": [{"nexthopAddr": "10.100.4.4", "interface": "Vlan4093"}], + }, + }, + } + } + } ], - - "inputs": {"routes_entries": [ - {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, - ]}, + "inputs": { + "routes_entries": [ + {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, + ] + }, "expected": {"result": "success"}, }, -{ + { "name": "Failure-invalid-route-type", "test": VerifyRouteType, "eos_data": [ @@ -538,17 +485,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.0.12", - "interface": "Ethernet1" - }, - { - "nexthopAddr": "10.100.0.14", - "interface": "Ethernet2" - } - ], - "directlyConnected": False + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "directlyConnected": False, }, "10.100.0.12/31": { "hardwareProgrammed": True, @@ -556,12 +494,8 @@ "routeLeaked": False, "kernelProgrammed": True, "routeAction": "forward", - "vias": [ - { - "interface": "Ethernet1" - } - ], - "directlyConnected": True + "vias": [{"interface": "Ethernet1"}], + "directlyConnected": True, }, "10.100.0.14/31": { "hardwareProgrammed": True, @@ -569,12 +503,8 @@ "routeLeaked": False, "kernelProgrammed": True, "routeAction": "forward", - "vias": [ - { - "interface": "Ethernet2" - } - ], - "directlyConnected": True + "vias": [{"interface": "Ethernet2"}], + "directlyConnected": True, }, "10.100.0.128/31": { "hardwareProgrammed": True, @@ -584,17 +514,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.0.12", - "interface": "Ethernet1" - }, - { - "nexthopAddr": "10.100.0.14", - "interface": "Ethernet2" - } - ], - "directlyConnected": False + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "directlyConnected": False, }, "10.100.1.5/32": { "hardwareProgrammed": True, @@ -605,45 +526,50 @@ "directlyConnected": False, "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.4.4", - "interface": "Vlan4093" - } - ] - }, } - - }}} + "vias": [{"nexthopAddr": "10.100.4.4", "interface": "Vlan4093"}], + }, + }, + } + } + } ], - - "inputs": {"routes_entries": [ - {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "iBGP"}, - {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "static"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "eBGP"}, - ]}, - "expected": {"result": "failure", "messages": ['For following routes, VRF is not configured or Route types are invalid:\n{\'routes_entries\': {\'10.10.0.1/32\': {\'default\': {\'route_type\': "Expected route type is \'iBGP\' however in actual it is found as \'eBGP\'"}}, \'10.100.0.14/31\': {\'default\': {\'route_type\': "Expected route type is \'static\' however in actual it is found as \'connected\'"}}, \'10.100.1.5/32\': {\'default\': {\'route_type\': "Expected route type is \'eBGP\' however in actual it is found as \'iBGP\'"}}}}']}, + "inputs": { + "routes_entries": [ + {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "iBGP"}, + {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "static"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "eBGP"}, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {'10.10.0.1/32': {'default': {'route_type': \"Expected route type is 'iBGP' however in actual it is found as 'eBGP'\"}}, '10.100.0.14/31': {'default': {'route_type': \"Expected route type is 'static' however in actual it is found as 'connected'\"}}, '10.100.1.5/32': {'default': {'route_type': \"Expected route type is 'eBGP' however in actual it is found as 'iBGP'\"}}}}" + ], + }, }, -{ + { "name": "Failure-vrf-not-configured", "test": VerifyRouteType, - "eos_data": [ - { - "vrfs": {} - } - ], - - "inputs": {"routes_entries": [ - {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, - ]}, - "expected": {"result": "failure", "messages": ["For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {'10.10.0.1/32': {'default': 'Not configured'}, '10.100.0.12/31': {'default': 'Not configured'}, '10.100.0.14/31': {'default': 'Not configured'}, '10.100.0.128/31': {'default': 'Not configured'}, '10.100.1.5/32': {'default': 'Not configured'}}}"]}, + "eos_data": [{"vrfs": {}}], + "inputs": { + "routes_entries": [ + {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {'10.10.0.1/32': {'default': 'Not configured'}, '10.100.0.12/31': {'default': 'Not configured'}, '10.100.0.14/31': {'default': 'Not configured'}, '10.100.0.128/31': {'default': 'Not configured'}, '10.100.1.5/32': {'default': 'Not configured'}}}" + ], + }, }, -{ + { "name": "Failure-invalid-network-address", "test": VerifyRouteType, "eos_data": [ @@ -663,17 +589,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.0.12", - "interface": "Ethernet1" - }, - { - "nexthopAddr": "10.100.0.14", - "interface": "Ethernet2" - } - ], - "directlyConnected": False + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "directlyConnected": False, }, "10.100.0.12/31": { "hardwareProgrammed": True, @@ -681,12 +598,8 @@ "routeLeaked": False, "kernelProgrammed": True, "routeAction": "forward", - "vias": [ - { - "interface": "Ethernet1" - } - ], - "directlyConnected": True + "vias": [{"interface": "Ethernet1"}], + "directlyConnected": True, }, "10.100.0.14/31": { "hardwareProgrammed": True, @@ -694,12 +607,8 @@ "routeLeaked": False, "kernelProgrammed": True, "routeAction": "forward", - "vias": [ - { - "interface": "Ethernet2" - } - ], - "directlyConnected": True + "vias": [{"interface": "Ethernet2"}], + "directlyConnected": True, }, "10.100.0.128/31": { "hardwareProgrammed": True, @@ -709,17 +618,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.0.12", - "interface": "Ethernet1" - }, - { - "nexthopAddr": "10.100.0.14", - "interface": "Ethernet2" - } - ], - "directlyConnected": False + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "directlyConnected": False, }, "10.100.1.5/32": { "hardwareProgrammed": True, @@ -730,33 +630,27 @@ "directlyConnected": False, "preference": 200, "metric": 0, - "vias": [ - { - "nexthopAddr": "10.100.4.4", - "interface": "Vlan4093" - } - ] - }, } - - }}} + "vias": [{"nexthopAddr": "10.100.4.4", "interface": "Vlan4093"}], + }, + }, + } + } + } ], - - "inputs": {"routes_entries": [ - {"vrf": "default", "prefix": "1022.10.0.1/32", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "2001:db8:3333:4444:5555:6666:7777:8888:", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, - ]}, - "expected": {"result": "error", "messages": ["Input is not a valid IPv4 network"]}, }, - - + "inputs": { + "routes_entries": [ + {"vrf": "default", "prefix": "1022.10.0.1/32", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "2001:db8:3333:4444:5555:6666:7777:8888:", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, + ] + }, + "expected": {"result": "error", "messages": ["Input is not a valid IPv4 network"]}, + }, ] - - - class TestVerifyRoutingTableSizeInputs: """Test anta.tests.routing.generic.VerifyRoutingTableSize.Input.""" From 4a7d9e1e8dfe0dc93c4f1e546101c27e0a13396e Mon Sep 17 00:00:00 2001 From: Geetanjali G Mane Date: Thu, 14 Nov 2024 13:40:25 +0530 Subject: [PATCH 03/10] Issue_884: Fixed linting issues observed in pre-commit run --- anta/tests/routing/generic.py | 13 ++-- .../units/anta_tests/routing/test_generic.py | 66 ++++++++++++------- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/anta/tests/routing/generic.py b/anta/tests/routing/generic.py index 3cbd372dc..3227bb3ba 100644 --- a/anta/tests/routing/generic.py +++ b/anta/tests/routing/generic.py @@ -9,7 +9,7 @@ from functools import cache from ipaddress import IPv4Address, IPv4Interface, IPv4Network -from typing import TYPE_CHECKING, ClassVar, Literal +from typing import Any, TYPE_CHECKING, ClassVar, Literal from pydantic import BaseModel, model_validator @@ -185,7 +185,7 @@ def test(self) -> None: class VerifyRouteType(AntaTest): - """Verifies the type of the provided route in the routing table of a specified VRF. + """Verifies the route-type of the provided prefixes within a specified VRF. Expected Results ---------------- @@ -213,7 +213,8 @@ class VerifyRouteType(AntaTest): route_type: eBGP - vrf: default prefix: 10.100.1.5/32 - route_type: iBGP``` + route_type: iBGP + ``` """ categories: ClassVar[list[str]] = ["routing"] @@ -228,7 +229,6 @@ class Input(AntaTest.Input): class Routes(BaseModel): """Model for a list of route entries.""" - vrf: str = "default" """ VRF context. Defaults to `default` VRF.""" prefix: IPv4Network @@ -242,7 +242,7 @@ def test(self) -> None: self.result.is_success() # Forming a dictionary for the test failure message. - failures: dict[str, any] = {"routes_entries": {}} + failures: dict[str, Any] = {"routes_entries": {}} # Collecting the 'show ip route vrf all' command output. output = self.instance_commands[0].json_output @@ -268,8 +268,7 @@ def test(self) -> None: # Verifying that the expected route-type and the actual routes are the same. if expected_route_type != actual_route_type: failures["routes_entries"][network] = { - vrf: {"route_type": f"Expected route type is '{expected_route_type}' " f"however in actual it is found as '{actual_route_type}'"} - } + vrf: {"route_type": f"Expected route type is '{expected_route_type}' " f"however in actual it is found as '{actual_route_type}'"}} # Updating the result, as per the testcase failure message. if failures["routes_entries"]: diff --git a/tests/units/anta_tests/routing/test_generic.py b/tests/units/anta_tests/routing/test_generic.py index b4d692bea..310d40f55 100644 --- a/tests/units/anta_tests/routing/test_generic.py +++ b/tests/units/anta_tests/routing/test_generic.py @@ -305,7 +305,7 @@ "expected": {"result": "failure", "messages": ["The following route(s) are missing from the routing table of VRF default: ['10.1.0.2']"]}, }, { - "name": "Failure-route-not-found", + "name": "Success-valid-route-type", "test": VerifyRouteType, "eos_data": [ { @@ -324,7 +324,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, + {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], "directlyConnected": False, }, "10.100.0.12/31": { @@ -353,7 +354,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, + {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], "directlyConnected": False, }, "10.100.1.5/32": { @@ -375,19 +377,16 @@ "inputs": { "routes_entries": [ {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.0.12/32", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, + {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, + {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, ] }, - "expected": { - "result": "failure", - "messages": [ - "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {" - "'10.100.0.12/32': {'default': 'Routes not found.'}}}" - ], - }, + "expected": {"result": "success"}, }, { - "name": "Success-valid-route-type", + "name": "Failure-route-not-found", "test": VerifyRouteType, "eos_data": [ { @@ -406,7 +405,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, + {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], "directlyConnected": False, }, "10.100.0.12/31": { @@ -435,7 +435,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, + {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], "directlyConnected": False, }, "10.100.1.5/32": { @@ -457,13 +458,16 @@ "inputs": { "routes_entries": [ {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, + {"vrf": "default", "prefix": "10.100.0.12/32", "route_type": "connected"}, ] }, - "expected": {"result": "success"}, + "expected": { + "result": "failure", + "messages": [ + "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {" + "'10.100.0.12/32': {'default': 'Routes not found.'}}}" + ], + }, }, { "name": "Failure-invalid-route-type", @@ -485,7 +489,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, + {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], "directlyConnected": False, }, "10.100.0.12/31": { @@ -514,7 +519,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, + {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], "directlyConnected": False, }, "10.100.1.5/32": { @@ -545,7 +551,10 @@ "expected": { "result": "failure", "messages": [ - "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {'10.10.0.1/32': {'default': {'route_type': \"Expected route type is 'iBGP' however in actual it is found as 'eBGP'\"}}, '10.100.0.14/31': {'default': {'route_type': \"Expected route type is 'static' however in actual it is found as 'connected'\"}}, '10.100.1.5/32': {'default': {'route_type': \"Expected route type is 'eBGP' however in actual it is found as 'iBGP'\"}}}}" + "For following routes, VRF is not configured or Route types are invalid:\n" + "{'routes_entries': {'10.10.0.1/32': {'default': {'route_type': \"Expected route type is 'iBGP' however in actual it is found as 'eBGP'\"}}, " + "'10.100.0.14/31': {'default': {'route_type': \"Expected route type is 'static' however in actual it is found as 'connected'\"}}, " + "'10.100.1.5/32': {'default': {'route_type': \"Expected route type is 'eBGP' however in actual it is found as 'iBGP'\"}}}}" ], }, }, @@ -565,7 +574,12 @@ "expected": { "result": "failure", "messages": [ - "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {'10.10.0.1/32': {'default': 'Not configured'}, '10.100.0.12/31': {'default': 'Not configured'}, '10.100.0.14/31': {'default': 'Not configured'}, '10.100.0.128/31': {'default': 'Not configured'}, '10.100.1.5/32': {'default': 'Not configured'}}}" + "For following routes, VRF is not configured or Route types are invalid:\n" + "{'routes_entries': {'10.10.0.1/32': {'default': 'Not configured'}, " + "'10.100.0.12/31': {'default': 'Not configured'}, " + "'10.100.0.14/31': {'default': 'Not configured'}, " + "'10.100.0.128/31': {'default': 'Not configured'}, " + "'10.100.1.5/32': {'default': 'Not configured'}}}" ], }, }, @@ -589,7 +603,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, + {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], "directlyConnected": False, }, "10.100.0.12/31": { @@ -618,7 +633,8 @@ "routeAction": "forward", "preference": 200, "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], + "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, + {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], "directlyConnected": False, }, "10.100.1.5/32": { From d97b92f4ebca41315d48e8a265d3a56b0a965adc Mon Sep 17 00:00:00 2001 From: Geetanjali G Mane Date: Tue, 10 Dec 2024 13:35:38 +0530 Subject: [PATCH 04/10] Issue_884: Updated documentation and opimized code --- anta/custom_types.py | 2 +- anta/input_models/routing/generic.py | 29 ++ anta/tests/routing/generic.py | 109 +++--- docs/api/tests.routing.generic.md | 15 + examples/tests.yaml | 11 + .../units/anta_tests/routing/test_generic.py | 343 +----------------- 6 files changed, 116 insertions(+), 393 deletions(-) create mode 100644 anta/input_models/routing/generic.py diff --git a/anta/custom_types.py b/anta/custom_types.py index a6972a727..ef0c28478 100644 --- a/anta/custom_types.py +++ b/anta/custom_types.py @@ -210,7 +210,7 @@ def validate_regex(value: str) -> str: ] # TODO: Needs to update the route types with confirmation. -RouteType = Literal[ +IPv4RouteType = Literal[ "connected", "static", "kernel", diff --git a/anta/input_models/routing/generic.py b/anta/input_models/routing/generic.py new file mode 100644 index 000000000..9f5ba0e66 --- /dev/null +++ b/anta/input_models/routing/generic.py @@ -0,0 +1,29 @@ +# 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. +"""Module containing input models for generic routing tests.""" + +from __future__ import annotations + +from ipaddress import IPv4Network + +from pydantic import BaseModel + +from anta.custom_types import IPv4RouteType + + +class IPv4Routes(BaseModel): + """Model for a list of IPV4 route entries.""" + + prefix: IPv4Network + """ IPV4network to validate the rout type. """ + + vrf: str = "default" + """ VRF context. Defaults to `default` VRF.""" + + route_type: IPv4RouteType + """ List of IPV4 Route type to validate the valid rout type. """ + + def __str__(self) -> str: + """Return a human-readable string representation of the route-type for reporting.""" + return f"Prefix: {self.prefix} VRF: {self.vrf}" diff --git a/anta/tests/routing/generic.py b/anta/tests/routing/generic.py index 3227bb3ba..ad2175f6a 100644 --- a/anta/tests/routing/generic.py +++ b/anta/tests/routing/generic.py @@ -8,12 +8,13 @@ from __future__ import annotations from functools import cache -from ipaddress import IPv4Address, IPv4Interface, IPv4Network -from typing import Any, TYPE_CHECKING, ClassVar, Literal +from ipaddress import IPv4Address, IPv4Interface +from typing import TYPE_CHECKING, ClassVar, Literal -from pydantic import BaseModel, model_validator +from pydantic import model_validator -from anta.custom_types import PositiveInteger, RouteType +from anta.custom_types import PositiveInteger +from anta.input_models.routing.generic import IPv4Routes from anta.models import AntaCommand, AntaTemplate, AntaTest from anta.tools import get_value @@ -184,92 +185,74 @@ def test(self) -> None: self.result.is_failure(f"The following route(s) are missing from the routing table of VRF {self.inputs.vrf}: {missing_routes}") -class VerifyRouteType(AntaTest): - """Verifies the route-type of the provided prefixes within a specified VRF. +class VerifyIPv4RouteType(AntaTest): + """Verifies the IPV4 route-type of the provided prefixes within a specified VRF. + + This test performs the following checks for each specific ipv4 route: + 1. Verifies that the specified VRF is configured. + 2. Verifies that the specified ipv4 route is exists in the configuration. + 3. Verifies that the the specified ipv4 route is of the expected type. Expected Results ---------------- - * Success: The test will pass if the provided route is of the expected type. - * Failure: The test will fail if the provided route is not of the expected type. + * Success: If all of the following conditions are met: + - All the specified VRFs are configured. + - All the specified ipv4 routes are found. + - All the specified ipv4 routes are of the expected type. + * Failure: If any of the following occur: + - A specified VRF is not configured. + - A specified ipv4 route is not found. + - Any specified ipv4 route is not of the expected type. Examples -------- ```yaml anta.tests.routing: generic: - VerifyRouteType: - routes_entries: - - vrf: default - prefix: 10.10.0.1/32 - route_type: eBGP - - vrf: default - prefix: 10.100.0.12/31 - route_type: connected - - vrf: default - prefix: 10.100.0.14/31 - route_type: connected - - vrf: default - prefix: 10.100.0.128/31 - route_type: eBGP - - vrf: default - prefix: 10.100.1.5/32 - route_type: iBGP + - VerifyIPV4RouteType: + routes_entries: + - prefix: 10.10.0.1/32 + vrf: default + route_type: eBGP + - prefix: 10.100.0.12/31 + vrf: default + route_type: connected + - prefix: 10.100.1.5/32 + vrf: default + route_type: iBGP ``` """ categories: ClassVar[list[str]] = ["routing"] - commands: ClassVar[list[AntaCommand | AntaTemplate]] = [ - AntaCommand(command="show ip route vrf all", revision=4), - ] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip route vrf all", revision=4)] class Input(AntaTest.Input): - """Input model for the VerifyRouteType test.""" - - routes_entries: list[Routes] + """Input model for the VerifyIPV4RouteType test.""" - class Routes(BaseModel): - """Model for a list of route entries.""" - vrf: str = "default" - """ VRF context. Defaults to `default` VRF.""" - prefix: IPv4Network - """ IPV4network to validate the rout type. """ - route_type: RouteType - """ List of Route type to validate the valid rout type. """ + routes_entries: list[IPv4Routes] @AntaTest.anta_test def test(self) -> None: - """Main test function for VerifyRouteType.""" + """Main test function for VerifyIPV4RouteType.""" self.result.is_success() - - # Forming a dictionary for the test failure message. - failures: dict[str, Any] = {"routes_entries": {}} - - # Collecting the 'show ip route vrf all' command output. output = self.instance_commands[0].json_output # Iterating over the all routes entries mentioned in the inputs. - for entries in self.inputs.routes_entries: - vrf = entries.vrf - network = str(entries.prefix) - expected_route_type = entries.route_type + for entry in self.inputs.routes_entries: + prefix = str(entry.prefix) + vrf = entry.vrf + expected_route_type = entry.route_type # Verifying that on device, expected VRF is configured. if (routes_details := get_value(output, f"vrfs.{vrf}.routes")) is None: - failures["routes_entries"][network] = {vrf: "Not configured"} + self.result.is_failure(f"{entry} - VRF not configured") continue - # Verifying that the expected route is present or not on the device - if (route_data := get_value(routes_details, network, separator="..")) is None: - failures["routes_entries"][network] = {vrf: "Routes not found."} + # Verifying that the expected ipv4 route is present or not on the device + if (route_data := routes_details.get(prefix)) is None: + self.result.is_failure(f"{entry} - Routes not found") continue - actual_route_type = route_data.get("routeType") - - # Verifying that the expected route-type and the actual routes are the same. - if expected_route_type != actual_route_type: - failures["routes_entries"][network] = { - vrf: {"route_type": f"Expected route type is '{expected_route_type}' " f"however in actual it is found as '{actual_route_type}'"}} - - # Updating the result, as per the testcase failure message. - if failures["routes_entries"]: - self.result.is_failure(f"For following routes, VRF is not configured or Route types are invalid:\n{failures}") + # Verifying that the specified ipv4 routes are of the expected type. + if expected_route_type != (actual_route_type := route_data.get("routeType")): + self.result.is_failure(f"{entry} - Incorrect route type, Expected: {expected_route_type} Actual: {actual_route_type}") diff --git a/docs/api/tests.routing.generic.md b/docs/api/tests.routing.generic.md index 1c4c39d8f..22153ca79 100644 --- a/docs/api/tests.routing.generic.md +++ b/docs/api/tests.routing.generic.md @@ -7,6 +7,8 @@ anta_title: ANTA catalog for generic routing tests ~ that can be found in the LICENSE file. --> +# Tests + ::: anta.tests.routing.generic options: show_root_heading: false @@ -18,3 +20,16 @@ anta_title: ANTA catalog for generic routing tests filters: - "!test" - "!render" + +# Input models + +::: anta.input_models.routing.generic + + options: + show_root_heading: false + show_root_toc_entry: false + show_bases: false + merge_init_into_class: false + anta_hide_test_module_description: true + show_labels: true + filters: ["!^__str__"] diff --git a/examples/tests.yaml b/examples/tests.yaml index e14b40823..1089faf51 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -502,6 +502,17 @@ anta.tests.routing.generic: # Verifies the size of the IP routing table of the default VRF. minimum: 2 maximum: 20 + - VerifyIPV4RouteType: + routes_entries: + - prefix: 10.10.0.1/32 + vrf: default + route_type: eBGP + - prefix: 10.100.0.12/31 + vrf: default + route_type: connected + - prefix: 10.100.1.5/32 + vrf: default + route_type: iBGP anta.tests.routing.isis: - VerifyISISInterfaceMode: # Verifies interface mode for IS-IS diff --git a/tests/units/anta_tests/routing/test_generic.py b/tests/units/anta_tests/routing/test_generic.py index 310d40f55..175abead3 100644 --- a/tests/units/anta_tests/routing/test_generic.py +++ b/tests/units/anta_tests/routing/test_generic.py @@ -11,7 +11,7 @@ import pytest from pydantic import ValidationError -from anta.tests.routing.generic import VerifyRouteType, VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize +from anta.tests.routing.generic import VerifyIPv4RouteType, VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize from tests.units.anta_tests import test DATA: list[dict[str, Any]] = [ @@ -306,70 +306,12 @@ }, { "name": "Success-valid-route-type", - "test": VerifyRouteType, + "test": VerifyIPv4RouteType, "eos_data": [ { "vrfs": { "default": { - "routingDisabled": False, - "allRoutesProgrammedHardware": True, - "allRoutesProgrammedKernel": True, - "defaultRouteState": "notSet", - "routes": { - "10.10.0.1/32": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, - {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], - "directlyConnected": False, - }, - "10.100.0.12/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [{"interface": "Ethernet1"}], - "directlyConnected": True, - }, - "10.100.0.14/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [{"interface": "Ethernet2"}], - "directlyConnected": True, - }, - "10.100.0.128/31": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, - {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], - "directlyConnected": False, - }, - "10.100.1.5/32": { - "hardwareProgrammed": True, - "routeType": "iBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "directlyConnected": False, - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.4.4", "interface": "Vlan4093"}], - }, - }, + "routes": {"10.10.0.1/32": {"routeType": "eBGP"}, "10.100.0.12/31": {"routeType": "connected"}, "10.100.1.5/32": {"routeType": "iBGP"}} } } } @@ -378,8 +320,6 @@ "routes_entries": [ {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, ] }, @@ -387,282 +327,27 @@ }, { "name": "Failure-route-not-found", - "test": VerifyRouteType, - "eos_data": [ - { - "vrfs": { - "default": { - "routingDisabled": False, - "allRoutesProgrammedHardware": True, - "allRoutesProgrammedKernel": True, - "defaultRouteState": "notSet", - "routes": { - "10.10.0.1/32": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, - {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], - "directlyConnected": False, - }, - "10.100.0.12/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [{"interface": "Ethernet1"}], - "directlyConnected": True, - }, - "10.100.0.14/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [{"interface": "Ethernet2"}], - "directlyConnected": True, - }, - "10.100.0.128/31": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, - {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], - "directlyConnected": False, - }, - "10.100.1.5/32": { - "hardwareProgrammed": True, - "routeType": "iBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "directlyConnected": False, - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.4.4", "interface": "Vlan4093"}], - }, - }, - } - } - } - ], - "inputs": { - "routes_entries": [ - {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.0.12/32", "route_type": "connected"}, - ] - }, - "expected": { - "result": "failure", - "messages": [ - "For following routes, VRF is not configured or Route types are invalid:\n{'routes_entries': {" - "'10.100.0.12/32': {'default': 'Routes not found.'}}}" - ], - }, + "test": VerifyIPv4RouteType, + "eos_data": [{"vrfs": {"default": {"routes": {}}}}], + "inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}]}, + "expected": {"result": "failure", "messages": ["Prefix: 10.10.0.1/32 VRF: default - Routes not found"]}, }, { "name": "Failure-invalid-route-type", - "test": VerifyRouteType, - "eos_data": [ - { - "vrfs": { - "default": { - "routingDisabled": False, - "allRoutesProgrammedHardware": True, - "allRoutesProgrammedKernel": True, - "defaultRouteState": "notSet", - "routes": { - "10.10.0.1/32": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, - {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], - "directlyConnected": False, - }, - "10.100.0.12/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [{"interface": "Ethernet1"}], - "directlyConnected": True, - }, - "10.100.0.14/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [{"interface": "Ethernet2"}], - "directlyConnected": True, - }, - "10.100.0.128/31": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, - {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], - "directlyConnected": False, - }, - "10.100.1.5/32": { - "hardwareProgrammed": True, - "routeType": "iBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "directlyConnected": False, - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.4.4", "interface": "Vlan4093"}], - }, - }, - } - } - } - ], - "inputs": { - "routes_entries": [ - {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "iBGP"}, - {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "static"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "eBGP"}, - ] - }, + "test": VerifyIPv4RouteType, + "eos_data": [{"vrfs": {"default": {"routes": {"10.10.0.1/32": {"routeType": "eBGP"}}}}}], + "inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "iBGP"}]}, "expected": { "result": "failure", - "messages": [ - "For following routes, VRF is not configured or Route types are invalid:\n" - "{'routes_entries': {'10.10.0.1/32': {'default': {'route_type': \"Expected route type is 'iBGP' however in actual it is found as 'eBGP'\"}}, " - "'10.100.0.14/31': {'default': {'route_type': \"Expected route type is 'static' however in actual it is found as 'connected'\"}}, " - "'10.100.1.5/32': {'default': {'route_type': \"Expected route type is 'eBGP' however in actual it is found as 'iBGP'\"}}}}" - ], + "messages": ["Prefix: 10.10.0.1/32 VRF: default - Incorrect route type, Expected: iBGP Actual: eBGP"], }, }, { "name": "Failure-vrf-not-configured", - "test": VerifyRouteType, + "test": VerifyIPv4RouteType, "eos_data": [{"vrfs": {}}], - "inputs": { - "routes_entries": [ - {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, - ] - }, - "expected": { - "result": "failure", - "messages": [ - "For following routes, VRF is not configured or Route types are invalid:\n" - "{'routes_entries': {'10.10.0.1/32': {'default': 'Not configured'}, " - "'10.100.0.12/31': {'default': 'Not configured'}, " - "'10.100.0.14/31': {'default': 'Not configured'}, " - "'10.100.0.128/31': {'default': 'Not configured'}, " - "'10.100.1.5/32': {'default': 'Not configured'}}}" - ], - }, - }, - { - "name": "Failure-invalid-network-address", - "test": VerifyRouteType, - "eos_data": [ - { - "vrfs": { - "default": { - "routingDisabled": False, - "allRoutesProgrammedHardware": True, - "allRoutesProgrammedKernel": True, - "defaultRouteState": "notSet", - "routes": { - "10.10.0.1/32": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, - {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], - "directlyConnected": False, - }, - "10.100.0.12/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [{"interface": "Ethernet1"}], - "directlyConnected": True, - }, - "10.100.0.14/31": { - "hardwareProgrammed": True, - "routeType": "connected", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "vias": [{"interface": "Ethernet2"}], - "directlyConnected": True, - }, - "10.100.0.128/31": { - "hardwareProgrammed": True, - "routeType": "eBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.0.12", "interface": "Ethernet1"}, - {"nexthopAddr": "10.100.0.14", "interface": "Ethernet2"}], - "directlyConnected": False, - }, - "10.100.1.5/32": { - "hardwareProgrammed": True, - "routeType": "iBGP", - "routeLeaked": False, - "kernelProgrammed": True, - "routeAction": "forward", - "directlyConnected": False, - "preference": 200, - "metric": 0, - "vias": [{"nexthopAddr": "10.100.4.4", "interface": "Vlan4093"}], - }, - }, - } - } - } - ], - "inputs": { - "routes_entries": [ - {"vrf": "default", "prefix": "1022.10.0.1/32", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "2001:db8:3333:4444:5555:6666:7777:8888:", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.14/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.0.128/31", "route_type": "eBGP"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, - ] - }, - "expected": {"result": "error", "messages": ["Input is not a valid IPv4 network"]}, + "inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}]}, + "expected": {"result": "failure", "messages": ["Prefix: 10.10.0.1/32 VRF: default - VRF not configured"]}, }, ] From 4396803d92d25ac54185b4a000df1256a9f5a4d6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:08:01 +0000 Subject: [PATCH 05/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/tests.yaml | 339 -------------------------------------------- 1 file changed, 339 deletions(-) diff --git a/examples/tests.yaml b/examples/tests.yaml index 1089faf51..108eb412b 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -489,342 +489,3 @@ anta.tests.routing.bgp: - address: aac1.ab5d.b41e vni: 10010 anta.tests.routing.generic: - - VerifyRoutingProtocolModel: - # Verifies the configured routing protocol model. - model: multi-agent - - VerifyRoutingTableEntry: - # Verifies that the provided routes are present in the routing table of a specified VRF. - vrf: default - routes: - - 10.1.0.1 - - 10.1.0.2 - - VerifyRoutingTableSize: - # Verifies the size of the IP routing table of the default VRF. - minimum: 2 - maximum: 20 - - VerifyIPV4RouteType: - routes_entries: - - prefix: 10.10.0.1/32 - vrf: default - route_type: eBGP - - prefix: 10.100.0.12/31 - vrf: default - route_type: connected - - prefix: 10.100.1.5/32 - vrf: default - route_type: iBGP -anta.tests.routing.isis: - - VerifyISISInterfaceMode: - # Verifies interface mode for IS-IS - interfaces: - - name: Loopback0 - mode: passive - # vrf is set to default by default - - name: Ethernet2 - mode: passive - level: 2 - # vrf is set to default by default - - name: Ethernet1 - mode: point-to-point - vrf: default - # level is set to 2 by default - - VerifyISISNeighborCount: - # Verifies number of IS-IS neighbors per level and per interface. - interfaces: - - name: Ethernet1 - level: 1 - count: 2 - - name: Ethernet2 - level: 2 - count: 1 - - name: Ethernet3 - count: 2 - # level is set to 2 by default - - VerifyISISNeighborState: - # Verifies all IS-IS neighbors are in UP state. - - VerifyISISSegmentRoutingAdjacencySegments: - # Verify that all expected Adjacency segments are correctly visible for each interface. - instances: - - name: CORE-ISIS - vrf: default - segments: - - interface: Ethernet2 - address: 10.0.1.3 - sid_origin: dynamic - - VerifyISISSegmentRoutingDataplane: - # Verify dataplane of a list of ISIS-SR instances. - instances: - - name: CORE-ISIS - vrf: default - dataplane: MPLS - - VerifyISISSegmentRoutingTunnels: - # Verify ISIS-SR tunnels computed by device. - entries: - # Check only endpoint - - endpoint: 1.0.0.122/32 - # Check endpoint and via TI-LFA - - endpoint: 1.0.0.13/32 - vias: - - type: tunnel - tunnel_id: ti-lfa - # Check endpoint and via IP routers - - endpoint: 1.0.0.14/32 - vias: - - type: ip - nexthop: 1.1.1.1 -anta.tests.routing.ospf: - - VerifyOSPFMaxLSA: - # Verifies all OSPF instances did not cross the maximum LSA threshold. - - VerifyOSPFNeighborCount: - # Verifies the number of OSPF neighbors in FULL state is the one we expect. - number: 3 - - VerifyOSPFNeighborState: - # Verifies all OSPF neighbors are in FULL state. -anta.tests.security: - - VerifyAPIHttpStatus: - # Verifies if eAPI HTTP server is disabled globally. - - VerifyAPIHttpsSSL: - # Verifies if the eAPI has a valid SSL profile. - profile: default - - VerifyAPIIPv4Acl: - # Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF. - number: 3 - vrf: default - - VerifyAPIIPv6Acl: - # Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF. - number: 3 - vrf: default - - VerifyAPISSLCertificate: - # Verifies the eAPI SSL certificate expiry, common subject name, encryption algorithm and key size. - certificates: - - certificate_name: ARISTA_SIGNING_CA.crt - expiry_threshold: 30 - common_name: AristaIT-ICA ECDSA Issuing Cert Authority - encryption_algorithm: ECDSA - key_size: 256 - - certificate_name: ARISTA_ROOT_CA.crt - expiry_threshold: 30 - common_name: Arista Networks Internal IT Root Cert Authority - encryption_algorithm: RSA - key_size: 4096 - - VerifyBannerLogin: - # Verifies the login banner of a device. - login_banner: | - # 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. - - VerifyBannerMotd: - # Verifies the motd banner of a device. - motd_banner: | - # 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. - - VerifyHardwareEntropy: - # Verifies hardware entropy generation is enabled on device. - - VerifyIPSecConnHealth: - # Verifies all IPv4 security connections. - - VerifyIPv4ACL: - # Verifies the configuration of IPv4 ACLs. - ipv4_access_lists: - - name: default-control-plane-acl - entries: - - sequence: 10 - action: permit icmp any any - - sequence: 20 - action: permit ip any any tracked - - sequence: 30 - action: permit udp any any eq bfd ttl eq 255 - - name: LabTest - entries: - - sequence: 10 - action: permit icmp any any - - sequence: 20 - action: permit tcp any any range 5900 5910 - - VerifySSHIPv4Acl: - # Verifies if the SSHD agent has IPv4 ACL(s) configured. - number: 3 - vrf: default - - VerifySSHIPv6Acl: - # Verifies if the SSHD agent has IPv6 ACL(s) configured. - number: 3 - vrf: default - - VerifySSHStatus: - # Verifies if the SSHD agent is disabled in the default VRF. - - VerifySpecificIPSecConn: - # Verifies the IPv4 security connections. - ip_security_connections: - - peer: 10.255.0.1 - - peer: 10.255.0.2 - vrf: default - connections: - - source_address: 100.64.3.2 - destination_address: 100.64.2.2 - - source_address: 172.18.3.2 - destination_address: 172.18.2.2 - - VerifyTelnetStatus: - # Verifies if Telnet is disabled in the default VRF. -anta.tests.services: - - VerifyDNSLookup: - # Verifies the DNS name to IP address resolution. - domain_names: - - arista.com - - www.google.com - - arista.ca - - VerifyDNSServers: - # Verifies if the DNS (Domain Name Service) servers are correctly configured. - dns_servers: - - server_address: 10.14.0.1 - vrf: default - priority: 1 - - server_address: 10.14.0.11 - vrf: MGMT - priority: 0 - - VerifyErrdisableRecovery: - # Verifies the errdisable recovery reason, status, and interval. - reasons: - - reason: acl - interval: 30 - - reason: bpduguard - interval: 30 - - VerifyHostname: - # Verifies the hostname of a device. - hostname: s1-spine1 -anta.tests.snmp: - - VerifySnmpContact: - # Verifies the SNMP contact of a device. - contact: Jon@example.com - - VerifySnmpErrorCounters: - # Verifies the SNMP error counters. - error_counters: - - inVersionErrs - - VerifySnmpIPv4Acl: - # Verifies if the SNMP agent has IPv4 ACL(s) configured. - number: 3 - vrf: default - - VerifySnmpIPv6Acl: - # Verifies if the SNMP agent has IPv6 ACL(s) configured. - number: 3 - vrf: default - - VerifySnmpLocation: - # Verifies the SNMP location of a device. - location: New York - - VerifySnmpPDUCounters: - # Verifies the SNMP PDU counters. - pdus: - - outTrapPdus - - inGetNextPdus - - VerifySnmpStatus: - # Verifies if the SNMP agent is enabled. - vrf: default -anta.tests.software: - - VerifyEOSExtensions: - # Verifies that all EOS extensions installed on the device are enabled for boot persistence. - - VerifyEOSVersion: - # Verifies the EOS version of the device. - versions: - - 4.25.4M - - 4.26.1F - - VerifyTerminAttrVersion: - # Verifies the TerminAttr version of the device. - versions: - - v1.13.6 - - v1.8.0 -anta.tests.stp: - - VerifySTPBlockedPorts: - # Verifies there is no STP blocked ports. - - VerifySTPCounters: - # Verifies there is no errors in STP BPDU packets. - - VerifySTPForwardingPorts: - # Verifies that all interfaces are forwarding for a provided list of VLAN(s). - vlans: - - 10 - - 20 - - VerifySTPMode: - # Verifies the configured STP mode for a provided list of VLAN(s). - mode: rapidPvst - vlans: - - 10 - - 20 - - VerifySTPRootPriority: - # Verifies the STP root priority for a provided list of VLAN or MST instance ID(s). - priority: 32768 - instances: - - 10 - - 20 - - VerifyStpTopologyChanges: - # Verifies the number of changes across all interfaces in the Spanning Tree Protocol (STP) topology is below a threshold. - threshold: 10 -anta.tests.stun: - - VerifyStunClient: - # (Deprecated) Verifies the translation for a source address on a STUN client. - stun_clients: - - source_address: 172.18.3.2 - public_address: 172.18.3.21 - source_port: 4500 - public_port: 6006 - - VerifyStunClientTranslation: - # Verifies the translation for a source address on a STUN client. - stun_clients: - - source_address: 172.18.3.2 - public_address: 172.18.3.21 - source_port: 4500 - public_port: 6006 - - source_address: 100.64.3.2 - public_address: 100.64.3.21 - source_port: 4500 - public_port: 6006 - - VerifyStunServer: - # Verifies the STUN server status is enabled and running. -anta.tests.system: - - VerifyAgentLogs: - # Verifies there are no agent crash reports. - - VerifyCPUUtilization: - # Verifies whether the CPU utilization is below 75%. - - VerifyCoredump: - # Verifies there are no core dump files. - - VerifyFileSystemUtilization: - # Verifies that no partition is utilizing more than 75% of its disk space. - - VerifyMemoryUtilization: - # Verifies whether the memory utilization is below 75%. - - VerifyNTP: - # Verifies if NTP is synchronised. - - VerifyNTPAssociations: - # Verifies the Network Time Protocol (NTP) associations. - 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 - - VerifyReloadCause: - # Verifies the last reload cause of the device. - - VerifyUptime: - # Verifies the device uptime. - minimum: 86400 -anta.tests.vlan: - - VerifyVlanInternalPolicy: - # Verifies the VLAN internal allocation policy and the range of VLANs. - policy: ascending - start_vlan_id: 1006 - end_vlan_id: 4094 -anta.tests.vxlan: - - VerifyVxlan1ConnSettings: - # Verifies the interface vxlan1 source interface and UDP port. - source_interface: Loopback1 - udp_port: 4789 - - VerifyVxlan1Interface: - # Verifies the Vxlan1 interface status. - - VerifyVxlanConfigSanity: - # Verifies there are no VXLAN config-sanity inconsistencies. - - VerifyVxlanVniBinding: - # Verifies the VNI-VLAN bindings of the Vxlan1 interface. - bindings: - 10010: 10 - 10020: 20 - - VerifyVxlanVtep: - # Verifies the VTEP peers of the Vxlan1 interface. - vteps: - - 10.1.1.5 - - 10.1.1.6 From 69e1e16a7397c2f3dce6d77cf40c9531bbd61cb4 Mon Sep 17 00:00:00 2001 From: Geetanjali G Mane Date: Tue, 10 Dec 2024 16:09:10 +0530 Subject: [PATCH 06/10] Issue_884: fixed pre-commit issues --- anta/tests/routing/generic.py | 6 +- examples/tests.yaml | 340 ++++++++++++++++++++++++++++++++++ 2 files changed, 343 insertions(+), 3 deletions(-) diff --git a/anta/tests/routing/generic.py b/anta/tests/routing/generic.py index ad2175f6a..0cedef120 100644 --- a/anta/tests/routing/generic.py +++ b/anta/tests/routing/generic.py @@ -209,7 +209,7 @@ class VerifyIPv4RouteType(AntaTest): ```yaml anta.tests.routing: generic: - - VerifyIPV4RouteType: + - VerifyIPv4RouteType: routes_entries: - prefix: 10.10.0.1/32 vrf: default @@ -227,13 +227,13 @@ class VerifyIPv4RouteType(AntaTest): commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip route vrf all", revision=4)] class Input(AntaTest.Input): - """Input model for the VerifyIPV4RouteType test.""" + """Input model for the VerifyIPv4RouteType test.""" routes_entries: list[IPv4Routes] @AntaTest.anta_test def test(self) -> None: - """Main test function for VerifyIPV4RouteType.""" + """Main test function for VerifyIPv4RouteType.""" self.result.is_success() output = self.instance_commands[0].json_output diff --git a/examples/tests.yaml b/examples/tests.yaml index 108eb412b..d4956f6a5 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -489,3 +489,343 @@ anta.tests.routing.bgp: - address: aac1.ab5d.b41e vni: 10010 anta.tests.routing.generic: + - VerifyIPv4RouteType: + # Verifies the IPV4 route-type of the provided prefixes within a specified VRF. + routes_entries: + - prefix: 10.10.0.1/32 + vrf: default + route_type: eBGP + - prefix: 10.100.0.12/31 + vrf: default + route_type: connected + - prefix: 10.100.1.5/32 + vrf: default + route_type: iBGP + - VerifyRoutingProtocolModel: + # Verifies the configured routing protocol model. + model: multi-agent + - VerifyRoutingTableEntry: + # Verifies that the provided routes are present in the routing table of a specified VRF. + vrf: default + routes: + - 10.1.0.1 + - 10.1.0.2 + - VerifyRoutingTableSize: + # Verifies the size of the IP routing table of the default VRF. + minimum: 2 + maximum: 20 +anta.tests.routing.isis: + - VerifyISISInterfaceMode: + # Verifies interface mode for IS-IS + interfaces: + - name: Loopback0 + mode: passive + # vrf is set to default by default + - name: Ethernet2 + mode: passive + level: 2 + # vrf is set to default by default + - name: Ethernet1 + mode: point-to-point + vrf: default + # level is set to 2 by default + - VerifyISISNeighborCount: + # Verifies number of IS-IS neighbors per level and per interface. + interfaces: + - name: Ethernet1 + level: 1 + count: 2 + - name: Ethernet2 + level: 2 + count: 1 + - name: Ethernet3 + count: 2 + # level is set to 2 by default + - VerifyISISNeighborState: + # Verifies all IS-IS neighbors are in UP state. + - VerifyISISSegmentRoutingAdjacencySegments: + # Verify that all expected Adjacency segments are correctly visible for each interface. + instances: + - name: CORE-ISIS + vrf: default + segments: + - interface: Ethernet2 + address: 10.0.1.3 + sid_origin: dynamic + - VerifyISISSegmentRoutingDataplane: + # Verify dataplane of a list of ISIS-SR instances. + instances: + - name: CORE-ISIS + vrf: default + dataplane: MPLS + - VerifyISISSegmentRoutingTunnels: + # Verify ISIS-SR tunnels computed by device. + entries: + # Check only endpoint + - endpoint: 1.0.0.122/32 + # Check endpoint and via TI-LFA + - endpoint: 1.0.0.13/32 + vias: + - type: tunnel + tunnel_id: ti-lfa + # Check endpoint and via IP routers + - endpoint: 1.0.0.14/32 + vias: + - type: ip + nexthop: 1.1.1.1 +anta.tests.routing.ospf: + - VerifyOSPFMaxLSA: + # Verifies all OSPF instances did not cross the maximum LSA threshold. + - VerifyOSPFNeighborCount: + # Verifies the number of OSPF neighbors in FULL state is the one we expect. + number: 3 + - VerifyOSPFNeighborState: + # Verifies all OSPF neighbors are in FULL state. +anta.tests.security: + - VerifyAPIHttpStatus: + # Verifies if eAPI HTTP server is disabled globally. + - VerifyAPIHttpsSSL: + # Verifies if the eAPI has a valid SSL profile. + profile: default + - VerifyAPIIPv4Acl: + # Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF. + number: 3 + vrf: default + - VerifyAPIIPv6Acl: + # Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF. + number: 3 + vrf: default + - VerifyAPISSLCertificate: + # Verifies the eAPI SSL certificate expiry, common subject name, encryption algorithm and key size. + certificates: + - certificate_name: ARISTA_SIGNING_CA.crt + expiry_threshold: 30 + common_name: AristaIT-ICA ECDSA Issuing Cert Authority + encryption_algorithm: ECDSA + key_size: 256 + - certificate_name: ARISTA_ROOT_CA.crt + expiry_threshold: 30 + common_name: Arista Networks Internal IT Root Cert Authority + encryption_algorithm: RSA + key_size: 4096 + - VerifyBannerLogin: + # Verifies the login banner of a device. + login_banner: | + # 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. + - VerifyBannerMotd: + # Verifies the motd banner of a device. + motd_banner: | + # 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. + - VerifyHardwareEntropy: + # Verifies hardware entropy generation is enabled on device. + - VerifyIPSecConnHealth: + # Verifies all IPv4 security connections. + - VerifyIPv4ACL: + # Verifies the configuration of IPv4 ACLs. + ipv4_access_lists: + - name: default-control-plane-acl + entries: + - sequence: 10 + action: permit icmp any any + - sequence: 20 + action: permit ip any any tracked + - sequence: 30 + action: permit udp any any eq bfd ttl eq 255 + - name: LabTest + entries: + - sequence: 10 + action: permit icmp any any + - sequence: 20 + action: permit tcp any any range 5900 5910 + - VerifySSHIPv4Acl: + # Verifies if the SSHD agent has IPv4 ACL(s) configured. + number: 3 + vrf: default + - VerifySSHIPv6Acl: + # Verifies if the SSHD agent has IPv6 ACL(s) configured. + number: 3 + vrf: default + - VerifySSHStatus: + # Verifies if the SSHD agent is disabled in the default VRF. + - VerifySpecificIPSecConn: + # Verifies the IPv4 security connections. + ip_security_connections: + - peer: 10.255.0.1 + - peer: 10.255.0.2 + vrf: default + connections: + - source_address: 100.64.3.2 + destination_address: 100.64.2.2 + - source_address: 172.18.3.2 + destination_address: 172.18.2.2 + - VerifyTelnetStatus: + # Verifies if Telnet is disabled in the default VRF. +anta.tests.services: + - VerifyDNSLookup: + # Verifies the DNS name to IP address resolution. + domain_names: + - arista.com + - www.google.com + - arista.ca + - VerifyDNSServers: + # Verifies if the DNS (Domain Name Service) servers are correctly configured. + dns_servers: + - server_address: 10.14.0.1 + vrf: default + priority: 1 + - server_address: 10.14.0.11 + vrf: MGMT + priority: 0 + - VerifyErrdisableRecovery: + # Verifies the errdisable recovery reason, status, and interval. + reasons: + - reason: acl + interval: 30 + - reason: bpduguard + interval: 30 + - VerifyHostname: + # Verifies the hostname of a device. + hostname: s1-spine1 +anta.tests.snmp: + - VerifySnmpContact: + # Verifies the SNMP contact of a device. + contact: Jon@example.com + - VerifySnmpErrorCounters: + # Verifies the SNMP error counters. + error_counters: + - inVersionErrs + - VerifySnmpIPv4Acl: + # Verifies if the SNMP agent has IPv4 ACL(s) configured. + number: 3 + vrf: default + - VerifySnmpIPv6Acl: + # Verifies if the SNMP agent has IPv6 ACL(s) configured. + number: 3 + vrf: default + - VerifySnmpLocation: + # Verifies the SNMP location of a device. + location: New York + - VerifySnmpPDUCounters: + # Verifies the SNMP PDU counters. + pdus: + - outTrapPdus + - inGetNextPdus + - VerifySnmpStatus: + # Verifies if the SNMP agent is enabled. + vrf: default +anta.tests.software: + - VerifyEOSExtensions: + # Verifies that all EOS extensions installed on the device are enabled for boot persistence. + - VerifyEOSVersion: + # Verifies the EOS version of the device. + versions: + - 4.25.4M + - 4.26.1F + - VerifyTerminAttrVersion: + # Verifies the TerminAttr version of the device. + versions: + - v1.13.6 + - v1.8.0 +anta.tests.stp: + - VerifySTPBlockedPorts: + # Verifies there is no STP blocked ports. + - VerifySTPCounters: + # Verifies there is no errors in STP BPDU packets. + - VerifySTPForwardingPorts: + # Verifies that all interfaces are forwarding for a provided list of VLAN(s). + vlans: + - 10 + - 20 + - VerifySTPMode: + # Verifies the configured STP mode for a provided list of VLAN(s). + mode: rapidPvst + vlans: + - 10 + - 20 + - VerifySTPRootPriority: + # Verifies the STP root priority for a provided list of VLAN or MST instance ID(s). + priority: 32768 + instances: + - 10 + - 20 + - VerifyStpTopologyChanges: + # Verifies the number of changes across all interfaces in the Spanning Tree Protocol (STP) topology is below a threshold. + threshold: 10 +anta.tests.stun: + - VerifyStunClient: + # (Deprecated) Verifies the translation for a source address on a STUN client. + stun_clients: + - source_address: 172.18.3.2 + public_address: 172.18.3.21 + source_port: 4500 + public_port: 6006 + - VerifyStunClientTranslation: + # Verifies the translation for a source address on a STUN client. + stun_clients: + - source_address: 172.18.3.2 + public_address: 172.18.3.21 + source_port: 4500 + public_port: 6006 + - source_address: 100.64.3.2 + public_address: 100.64.3.21 + source_port: 4500 + public_port: 6006 + - VerifyStunServer: + # Verifies the STUN server status is enabled and running. +anta.tests.system: + - VerifyAgentLogs: + # Verifies there are no agent crash reports. + - VerifyCPUUtilization: + # Verifies whether the CPU utilization is below 75%. + - VerifyCoredump: + # Verifies there are no core dump files. + - VerifyFileSystemUtilization: + # Verifies that no partition is utilizing more than 75% of its disk space. + - VerifyMemoryUtilization: + # Verifies whether the memory utilization is below 75%. + - VerifyNTP: + # Verifies if NTP is synchronised. + - VerifyNTPAssociations: + # Verifies the Network Time Protocol (NTP) associations. + 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 + - VerifyReloadCause: + # Verifies the last reload cause of the device. + - VerifyUptime: + # Verifies the device uptime. + minimum: 86400 +anta.tests.vlan: + - VerifyVlanInternalPolicy: + # Verifies the VLAN internal allocation policy and the range of VLANs. + policy: ascending + start_vlan_id: 1006 + end_vlan_id: 4094 +anta.tests.vxlan: + - VerifyVxlan1ConnSettings: + # Verifies the interface vxlan1 source interface and UDP port. + source_interface: Loopback1 + udp_port: 4789 + - VerifyVxlan1Interface: + # Verifies the Vxlan1 interface status. + - VerifyVxlanConfigSanity: + # Verifies there are no VXLAN config-sanity inconsistencies. + - VerifyVxlanVniBinding: + # Verifies the VNI-VLAN bindings of the Vxlan1 interface. + bindings: + 10010: 10 + 10020: 20 + - VerifyVxlanVtep: + # Verifies the VTEP peers of the Vxlan1 interface. + vteps: + - 10.1.1.5 + - 10.1.1.6 From 4ad14e49a434c4b1bc78b9c60109ccde6ff43b2a Mon Sep 17 00:00:00 2001 From: Geetanjali G Mane Date: Tue, 10 Dec 2024 18:18:38 +0530 Subject: [PATCH 07/10] Issue_884: Updated docstrings and added unit test for multi VRF --- anta/input_models/routing/generic.py | 13 ++++++------- anta/tests/routing/generic.py | 6 +++--- examples/tests.yaml | 2 +- tests/units/anta_tests/routing/test_generic.py | 9 ++++----- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/anta/input_models/routing/generic.py b/anta/input_models/routing/generic.py index 9f5ba0e66..41c78a132 100644 --- a/anta/input_models/routing/generic.py +++ b/anta/input_models/routing/generic.py @@ -7,7 +7,7 @@ from ipaddress import IPv4Network -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from anta.custom_types import IPv4RouteType @@ -15,15 +15,14 @@ class IPv4Routes(BaseModel): """Model for a list of IPV4 route entries.""" + model_config = ConfigDict(extra="forbid") prefix: IPv4Network - """ IPV4network to validate the rout type. """ - + """The IPV4 network to validate the route type.""" vrf: str = "default" - """ VRF context. Defaults to `default` VRF.""" - + """VRF context. Defaults to `default` VRF.""" route_type: IPv4RouteType - """ List of IPV4 Route type to validate the valid rout type. """ + """List of IPV4 Route type to validate the valid rout type.""" def __str__(self) -> str: - """Return a human-readable string representation of the route-type for reporting.""" + """Return a human-readable string representation of the IPv4RouteType for reporting.""" return f"Prefix: {self.prefix} VRF: {self.vrf}" diff --git a/anta/tests/routing/generic.py b/anta/tests/routing/generic.py index 0cedef120..edc02a45a 100644 --- a/anta/tests/routing/generic.py +++ b/anta/tests/routing/generic.py @@ -186,9 +186,9 @@ def test(self) -> None: class VerifyIPv4RouteType(AntaTest): - """Verifies the IPV4 route-type of the provided prefixes within a specified VRF. + """Verifies the route-type of the IPv4 prefixes. - This test performs the following checks for each specific ipv4 route: + This test performs the following checks for each ipv4 route: 1. Verifies that the specified VRF is configured. 2. Verifies that the specified ipv4 route is exists in the configuration. 3. Verifies that the the specified ipv4 route is of the expected type. @@ -255,4 +255,4 @@ def test(self) -> None: # Verifying that the specified ipv4 routes are of the expected type. if expected_route_type != (actual_route_type := route_data.get("routeType")): - self.result.is_failure(f"{entry} - Incorrect route type, Expected: {expected_route_type} Actual: {actual_route_type}") + self.result.is_failure(f"{entry} - Incorrect route type - Expected: {expected_route_type} Actual: {actual_route_type}") diff --git a/examples/tests.yaml b/examples/tests.yaml index d4956f6a5..623f16d24 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -490,7 +490,7 @@ anta.tests.routing.bgp: vni: 10010 anta.tests.routing.generic: - VerifyIPv4RouteType: - # Verifies the IPV4 route-type of the provided prefixes within a specified VRF. + # Verifies the route-type of the IPv4 prefixes. routes_entries: - prefix: 10.10.0.1/32 vrf: default diff --git a/tests/units/anta_tests/routing/test_generic.py b/tests/units/anta_tests/routing/test_generic.py index 175abead3..558b1164f 100644 --- a/tests/units/anta_tests/routing/test_generic.py +++ b/tests/units/anta_tests/routing/test_generic.py @@ -310,9 +310,8 @@ "eos_data": [ { "vrfs": { - "default": { - "routes": {"10.10.0.1/32": {"routeType": "eBGP"}, "10.100.0.12/31": {"routeType": "connected"}, "10.100.1.5/32": {"routeType": "iBGP"}} - } + "default": {"routes": {"10.10.0.1/32": {"routeType": "eBGP"}, "10.100.0.12/31": {"routeType": "connected"}}}, + "MGMT": {"routes": {"10.100.1.5/32": {"routeType": "iBGP"}}}, } } ], @@ -320,7 +319,7 @@ "routes_entries": [ {"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}, {"vrf": "default", "prefix": "10.100.0.12/31", "route_type": "connected"}, - {"vrf": "default", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, + {"vrf": "MGMT", "prefix": "10.100.1.5/32", "route_type": "iBGP"}, ] }, "expected": {"result": "success"}, @@ -339,7 +338,7 @@ "inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "iBGP"}]}, "expected": { "result": "failure", - "messages": ["Prefix: 10.10.0.1/32 VRF: default - Incorrect route type, Expected: iBGP Actual: eBGP"], + "messages": ["Prefix: 10.10.0.1/32 VRF: default - Incorrect route type - Expected: iBGP Actual: eBGP"], }, }, { From 7d9b6d2e6ffcb8034b18128fa347e122be8e2373 Mon Sep 17 00:00:00 2001 From: Guillaume Mulocher Date: Wed, 18 Dec 2024 23:08:13 +0100 Subject: [PATCH 08/10] Update anta/custom_types.py --- anta/custom_types.py | 1 - 1 file changed, 1 deletion(-) diff --git a/anta/custom_types.py b/anta/custom_types.py index ef0c28478..297f1f5a8 100644 --- a/anta/custom_types.py +++ b/anta/custom_types.py @@ -208,7 +208,6 @@ def validate_regex(value: str) -> str: SnmpErrorCounter = Literal[ "inVersionErrs", "inBadCommunityNames", "inBadCommunityUses", "inParseErrs", "outTooBigErrs", "outNoSuchNameErrs", "outBadValueErrs", "outGeneralErrs" ] -# TODO: Needs to update the route types with confirmation. IPv4RouteType = Literal[ "connected", From 3c434d781eef7675ffe840f942d7d5646c6b247b Mon Sep 17 00:00:00 2001 From: Guillaume Mulocher Date: Wed, 18 Dec 2024 23:08:56 +0100 Subject: [PATCH 09/10] Update anta/tests/routing/generic.py --- anta/tests/routing/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anta/tests/routing/generic.py b/anta/tests/routing/generic.py index edc02a45a..ff0ae184b 100644 --- a/anta/tests/routing/generic.py +++ b/anta/tests/routing/generic.py @@ -188,7 +188,7 @@ def test(self) -> None: class VerifyIPv4RouteType(AntaTest): """Verifies the route-type of the IPv4 prefixes. - This test performs the following checks for each ipv4 route: + This test performs the following checks for each IPv4 route: 1. Verifies that the specified VRF is configured. 2. Verifies that the specified ipv4 route is exists in the configuration. 3. Verifies that the the specified ipv4 route is of the expected type. From 58bcf8a93d92ca9908dba3d8dc8f1d04a7dd5349 Mon Sep 17 00:00:00 2001 From: Guillaume Mulocher Date: Wed, 18 Dec 2024 23:13:59 +0100 Subject: [PATCH 10/10] Apply suggestions from code review --- anta/tests/routing/generic.py | 18 +++++++++--------- docs/api/tests.routing.generic.md | 1 + tests/units/anta_tests/routing/test_generic.py | 10 +++++----- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/anta/tests/routing/generic.py b/anta/tests/routing/generic.py index ff0ae184b..7b916a3bc 100644 --- a/anta/tests/routing/generic.py +++ b/anta/tests/routing/generic.py @@ -190,19 +190,19 @@ class VerifyIPv4RouteType(AntaTest): This test performs the following checks for each IPv4 route: 1. Verifies that the specified VRF is configured. - 2. Verifies that the specified ipv4 route is exists in the configuration. - 3. Verifies that the the specified ipv4 route is of the expected type. + 2. Verifies that the specified IPv4 route is exists in the configuration. + 3. Verifies that the the specified IPv4 route is of the expected type. Expected Results ---------------- * Success: If all of the following conditions are met: - All the specified VRFs are configured. - - All the specified ipv4 routes are found. - - All the specified ipv4 routes are of the expected type. + - All the specified IPv4 routes are found. + - All the specified IPv4 routes are of the expected type. * Failure: If any of the following occur: - A specified VRF is not configured. - - A specified ipv4 route is not found. - - Any specified ipv4 route is not of the expected type. + - A specified IPv4 route is not found. + - Any specified IPv4 route is not of the expected type. Examples -------- @@ -248,11 +248,11 @@ def test(self) -> None: self.result.is_failure(f"{entry} - VRF not configured") continue - # Verifying that the expected ipv4 route is present or not on the device + # Verifying that the expected IPv4 route is present or not on the device if (route_data := routes_details.get(prefix)) is None: - self.result.is_failure(f"{entry} - Routes not found") + self.result.is_failure(f"{entry} - Route not found") continue - # Verifying that the specified ipv4 routes are of the expected type. + # Verifying that the specified IPv4 routes are of the expected type. if expected_route_type != (actual_route_type := route_data.get("routeType")): self.result.is_failure(f"{entry} - Incorrect route type - Expected: {expected_route_type} Actual: {actual_route_type}") diff --git a/docs/api/tests.routing.generic.md b/docs/api/tests.routing.generic.md index 22153ca79..bbc89040a 100644 --- a/docs/api/tests.routing.generic.md +++ b/docs/api/tests.routing.generic.md @@ -10,6 +10,7 @@ anta_title: ANTA catalog for generic routing tests # Tests ::: anta.tests.routing.generic + options: show_root_heading: false show_root_toc_entry: false diff --git a/tests/units/anta_tests/routing/test_generic.py b/tests/units/anta_tests/routing/test_generic.py index 558b1164f..4e9d654dc 100644 --- a/tests/units/anta_tests/routing/test_generic.py +++ b/tests/units/anta_tests/routing/test_generic.py @@ -305,7 +305,7 @@ "expected": {"result": "failure", "messages": ["The following route(s) are missing from the routing table of VRF default: ['10.1.0.2']"]}, }, { - "name": "Success-valid-route-type", + "name": "success-valid-route-type", "test": VerifyIPv4RouteType, "eos_data": [ { @@ -325,14 +325,14 @@ "expected": {"result": "success"}, }, { - "name": "Failure-route-not-found", + "name": "failure-route-not-found", "test": VerifyIPv4RouteType, "eos_data": [{"vrfs": {"default": {"routes": {}}}}], "inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}]}, - "expected": {"result": "failure", "messages": ["Prefix: 10.10.0.1/32 VRF: default - Routes not found"]}, + "expected": {"result": "failure", "messages": ["Prefix: 10.10.0.1/32 VRF: default - Route not found"]}, }, { - "name": "Failure-invalid-route-type", + "name": "failure-invalid-route-type", "test": VerifyIPv4RouteType, "eos_data": [{"vrfs": {"default": {"routes": {"10.10.0.1/32": {"routeType": "eBGP"}}}}}], "inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "iBGP"}]}, @@ -342,7 +342,7 @@ }, }, { - "name": "Failure-vrf-not-configured", + "name": "failure-vrf-not-configured", "test": VerifyIPv4RouteType, "eos_data": [{"vrfs": {}}], "inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}]},