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 2 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.


RouteType = Literal[
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
RouteType = Literal[
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",
]
99 changes: 96 additions & 3 deletions anta/tests/routing/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
from __future__ import annotations

from functools import cache
from ipaddress import IPv4Address, IPv4Interface
from ipaddress import IPv4Address, IPv4Interface, IPv4Network
from typing import TYPE_CHECKING, ClassVar, Literal

from pydantic import model_validator
from pydantic import BaseModel, model_validator

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
Expand Down Expand Up @@ -181,3 +182,95 @@ 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 VerifyRouteType(AntaTest):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
class VerifyRouteType(AntaTest):
class VerifyIPv4RouteType(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),
]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would expect this can be one line with out ruff config - maybe you need to check your IDE for the settings (you need to remove the , after revision=4) to let ruff do its magic


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. """
Copy link
Collaborator

Choose a reason for hiding this comment

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

so there is an effort now to move the nested base models to input_models - if you can move this to the relevant files that would be great (otherwise you have done exactly what we used to do so that was perfect)


@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.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
# 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:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
for entries in self.inputs.routes_entries:
for entry 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:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
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:
# Verifying that the expected route-type and the actual routes are the same.
if expected_route_type != ( actual_route_type := route_data.get("routeType")):

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}")
17 changes: 17 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,23 @@ anta.tests.routing:
routes:
- 10.1.0.1
- 10.1.0.2
- VerifyRouteType:
routes_entries:
- vrf: default
prefix: 10.10.0.1/32
route_type: eBGP
Copy link
Collaborator

Choose a reason for hiding this comment

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

  • prefix: 10.10.0.1/32
    vrf: default
    route_type: eBGP

Copy link
Collaborator

Choose a reason for hiding this comment

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

Update all places

- 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
bgp:
- VerifyBGPPeerCount:
address_families:
Expand Down
Loading
Loading