Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(anta): Added testcases to verifying Route Type #925

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions anta/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,34 @@ 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my information, how did you come up with this list of route-types?
This is good to help for the input but it may be tricky to maintain (though I suppose it is life)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @gmuloc ,
The mentioned route types are collected from the following command output.

eaf2-dc1#show ip route vrf all

VRF: default
Codes: C - connected, S - static, K - kernel, 
       O - OSPF, IA - OSPF inter area, E1 - OSPF external type 1,
       E2 - OSPF external type 2, N1 - OSPF NSSA external type 1,
       N2 - OSPF NSSA external type2, B - Other BGP Routes,
       B I - iBGP, B E - eBGP, R - RIP, I L1 - IS-IS level 1,
       I L2 - IS-IS level 2, O3 - OSPFv3, A B - BGP Aggregate,
       A O - OSPF Summary, NG - Nexthop Group Static Route,
       V - VXLAN Control Service, M - Martian,
       DH - DHCP client installed default route,
       DP - Dynamic Policy Route, L - VRF Leaked,
       G  - gRIBI, RC - Route Cache Route,
       CL - CBF Leaked Route

Gateway of last resort is not set

 B E      10.10.0.1/32 [200/0] via 10.100.0.12, Ethernet1
                               via 10.100.0.14, Ethernet2
 C        10.100.0.12/31 is directly connected, Ethernet1
 C        10.100.0.14/31 is directly connected, Ethernet2
 

Vitthal shared a JSON output with me, so with that help, I verified a few route types, such as connected, iBGP, and eBGP, to validate that the other route type needs some more configuration. Hence, I added TODO over there.


IPv4RouteType = 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",
]
29 changes: 29 additions & 0 deletions anta/input_models/routing/generic.py
Original file line number Diff line number Diff line change
@@ -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}"
75 changes: 75 additions & 0 deletions anta/tests/routing/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
from pydantic import model_validator

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

if TYPE_CHECKING:
import sys
Expand Down Expand Up @@ -181,3 +183,76 @@ def test(self) -> None:
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}")


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

class Input(AntaTest.Input):
"""Input model for the VerifyIPV4RouteType test."""

routes_entries: list[IPv4Routes]

@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifyIPV4RouteType."""
self.result.is_success()
output = self.instance_commands[0].json_output

# Iterating over the all routes entries mentioned in the inputs.
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:
self.result.is_failure(f"{entry} - VRF not configured")
continue

# 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

# 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}")
14 changes: 14 additions & 0 deletions docs/api/tests.routing.generic.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -18,3 +20,15 @@ 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**"]
gmuloc marked this conversation as resolved.
Show resolved Hide resolved
11 changes: 11 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,17 @@ anta.tests.routing:
routes:
- 10.1.0.1
- 10.1.0.2
- 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
bgp:
- VerifyBGPPeerCount:
address_families:
Expand Down
47 changes: 46 additions & 1 deletion tests/units/anta_tests/routing/test_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 VerifyIPv4RouteType, VerifyRoutingProtocolModel, VerifyRoutingTableEntry, VerifyRoutingTableSize
from tests.units.anta_tests import test

DATA: list[dict[str, Any]] = [
Expand Down Expand Up @@ -304,6 +304,51 @@
"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": "Success-valid-route-type",
"test": VerifyIPv4RouteType,
"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"}}
}
}
}
],
"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.1.5/32", "route_type": "iBGP"},
]
},
"expected": {"result": "success"},
},
{
"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"]},
},
{
"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"}]},
"expected": {
"result": "failure",
"messages": ["Prefix: 10.10.0.1/32 VRF: default - Incorrect route type, Expected: iBGP Actual: eBGP"],
},
},
{
"name": "Failure-vrf-not-configured",
"test": VerifyIPv4RouteType,
"eos_data": [{"vrfs": {}}],
"inputs": {"routes_entries": [{"vrf": "default", "prefix": "10.10.0.1/32", "route_type": "eBGP"}]},
"expected": {"result": "failure", "messages": ["Prefix: 10.10.0.1/32 VRF: default - VRF not configured"]},
},
]


Expand Down
Loading