1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
# 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.
"""
Generic routing test functions
"""
from __future__ import annotations
from ipaddress import IPv4Address, ip_interface
# Need to keep List for pydantic in python 3.8
from typing import List, Literal
from pydantic import model_validator
from anta.models import AntaCommand, AntaTemplate, AntaTest
# Mypy does not understand AntaTest.Input typing
# mypy: disable-error-code=attr-defined
class VerifyRoutingProtocolModel(AntaTest):
"""
Verifies the configured routing protocol model is the one we expect.
And if there is no mismatch between the configured and operating routing protocol model.
"""
name = "VerifyRoutingProtocolModel"
description = "Verifies the configured routing protocol model."
categories = ["routing"]
commands = [AntaCommand(command="show ip route summary", revision=3)]
class Input(AntaTest.Input): # pylint: disable=missing-class-docstring
model: Literal["multi-agent", "ribd"] = "multi-agent"
"""Expected routing protocol model"""
@AntaTest.anta_test
def test(self) -> None:
command_output = self.instance_commands[0].json_output
configured_model = command_output["protoModelStatus"]["configuredProtoModel"]
operating_model = command_output["protoModelStatus"]["operatingProtoModel"]
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}")
class VerifyRoutingTableSize(AntaTest):
"""
Verifies the size of the IP routing table (default VRF).
Should be between the two provided thresholds.
"""
name = "VerifyRoutingTableSize"
description = "Verifies the size of the IP routing table (default VRF). Should be between the two provided thresholds."
categories = ["routing"]
commands = [AntaCommand(command="show ip route summary", revision=3)]
class Input(AntaTest.Input): # pylint: disable=missing-class-docstring
minimum: int
"""Expected minimum routing table (default VRF) size"""
maximum: int
"""Expected maximum routing table (default VRF) size"""
@model_validator(mode="after") # type: ignore
def check_min_max(self) -> AntaTest.Input:
"""Validate that maximum is greater than minimum"""
if self.minimum > self.maximum:
raise ValueError(f"Minimum {self.minimum} is greater than maximum {self.maximum}")
return self
@AntaTest.anta_test
def test(self) -> None:
command_output = self.instance_commands[0].json_output
total_routes = int(command_output["vrfs"]["default"]["totalRoutes"])
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})")
class VerifyRoutingTableEntry(AntaTest):
"""
This test verifies that the provided routes are present in the routing table of a specified VRF.
Expected Results:
* success: The test will pass if the provided routes are present in the routing table.
* failure: The test will fail if one or many provided routes are missing from the routing table.
"""
name = "VerifyRoutingTableEntry"
description = "Verifies that the provided routes are present in the routing table of a specified VRF."
categories = ["routing"]
commands = [AntaTemplate(template="show ip route vrf {vrf} {route}")]
class Input(AntaTest.Input): # pylint: disable=missing-class-docstring
vrf: str = "default"
"""VRF context"""
routes: List[IPv4Address]
"""Routes to verify"""
def render(self, template: AntaTemplate) -> list[AntaCommand]:
return [template.render(vrf=self.inputs.vrf, route=route) for route in self.inputs.routes]
@AntaTest.anta_test
def test(self) -> None:
missing_routes = []
for command in self.instance_commands:
if "vrf" in command.params and "route" in command.params:
vrf, route = command.params["vrf"], command.params["route"]
if len(routes := command.json_output["vrfs"][vrf]["routes"]) == 0 or route != ip_interface(list(routes)[0]).ip:
missing_routes.append(str(route))
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}")
|