From 1f3364053864f7585d9d776c6480ef37d59c0870 Mon Sep 17 00:00:00 2001 From: VitthalMagadum Date: Tue, 3 Sep 2024 03:49:46 -0400 Subject: [PATCH 1/9] issue_811 Added TC for BGP route origin --- anta/tests/routing/bgp.py | 93 +++++++- examples/tests.yaml | 16 ++ tests/units/anta_tests/routing/test_bgp.py | 237 +++++++++++++++++++++ 3 files changed, 345 insertions(+), 1 deletion(-) diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 6a7002356..cdd3ed593 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -8,7 +8,7 @@ from __future__ import annotations from ipaddress import IPv4Address, IPv4Network, IPv6Address -from typing import Any, ClassVar +from typing import Any, ClassVar, Literal from pydantic import BaseModel, Field, PositiveInt, model_validator from pydantic.v1.utils import deep_update @@ -1404,3 +1404,94 @@ def test(self) -> None: self.result.is_success() else: self.result.is_failure(f"The following BGP peers are not configured or have non-zero update error counters:\n{failures}") + + +class VerifyBGPRouteOrigin(AntaTest): + """Verifies BGP route origin for the provided IPv4 Network(s). + + Expected Results + ---------------- + * Success: The test will pass if the BGP route's origin matches expected origin type. + * Failure: The test will fail if the BGP route's origin does not matches with expected origin type or BGP route entry(s) not found. + + Examples + -------- + ```yaml + anta.tests.routing: + bgp: + - VerifyBGPRouteOrigin: + route_entries: + - prefix: 10.100.0.128/31 + vrf: default + nexthop: 10.100.0.10 + origin: Igp + - prefix: 10.100.0.130/31 + vrf: default + nexthop: 10.100.0.8 + origin: Egp + ``` + """ + + name = "VerifyBGPRouteOrigin" + description = "Verifies BGP route origin for the provided IPv4 Network(s)." + categories: ClassVar[list[str]] = ["bgp"] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaTemplate(template="show ip bgp {prefix} vrf {vrf}", revision=3)] + + class Input(AntaTest.Input): + """Input model for the VerifyBGPRouteOrigin test.""" + + route_entries: list[BgpRoute] + """List of BGP route(s)""" + + class BgpRoute(BaseModel): + """Model for a BGP route(s).""" + + prefix: IPv4Network + """IPv4 network address""" + vrf: str = "default" + """Optional VRF. If not provided, it defaults to `default`.""" + paths: list[dict[str, IPv4Address | Literal["Igp", "Egp", "Incomplete"]]] + """A list of dictionaries represents a BGP path. + - `nexthop`: The next-hop IP address for the path. + - `origin`: The origin of the route.""" + + def render(self, template: AntaTemplate) -> list[AntaCommand]: + """Render the template for each BGP peer in the input list.""" + return [template.render(prefix=route.prefix, vrf=route.vrf) for route in self.inputs.route_entries] + + @AntaTest.anta_test + def test(self) -> None: + """Main test function for VerifyBGPRouteOrigin.""" + failures: dict[Any, Any] = {} + + for command, input_entry in zip(self.instance_commands, self.inputs.route_entries): + prefix = str(command.params.prefix) + vrf = command.params.vrf + paths = input_entry.paths + + # Verify if a BGP peer is configured with the provided vrf + if not (bgp_routes := get_value(command.json_output, f"vrfs..{vrf}..bgpRouteEntries..{prefix}..bgpRoutePaths", separator="..")): + failures[prefix] = {vrf: "Not configured"} + continue + + # Iterating over each nexthop. + failure: dict[Any, Any] = {} + for path in paths: + nexthop = str(path["nexthop"]) + origin = path["origin"] + if not (route_path := get_item(bgp_routes, "nextHop", nexthop)): + failure[nexthop] = "Path not found." + continue + + if (actual_origin := route_path.get("routeDetail", {}).get("origin", "Not Found")) != origin: + failure[nexthop] = f"Expected `{origin}` as the origin, but found `{actual_origin}` instead." + + # Updating failures. + if failure: + failures[prefix] = failure + + # Check if any failures + if not failures: + self.result.is_success() + else: + self.result.is_failure(f"Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n{failures}") diff --git a/examples/tests.yaml b/examples/tests.yaml index c5f87fae7..2f03ea095 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -593,6 +593,22 @@ anta.tests.routing: update_errors: - inUpdErrWithdraw - inUpdErrIgnore + - VerifyBGPRouteOrigin: + route_entries: + - prefix: 10.100.0.128/31 + vrf: default + paths: + - nexthop: 10.100.0.10 + origin: Igp + - nexthop: 10.100.4.5 + origin: Incomplete + - prefix: 10.100.0.130/31 + vrf: default + paths: + - nexthop: 10.100.0.8 + origin: Igp + - nexthop: 10.100.0.10 + origin: Igp ospf: - VerifyOSPFNeighborState: - VerifyOSPFNeighborCount: diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py index 47db8e60b..b9a0d1e4e 100644 --- a/tests/units/anta_tests/routing/test_bgp.py +++ b/tests/units/anta_tests/routing/test_bgp.py @@ -21,6 +21,7 @@ VerifyBGPPeerRouteRefreshCap, VerifyBGPPeersHealth, VerifyBGPPeerUpdateErrors, + VerifyBGPRouteOrigin, VerifyBGPSpecificPeers, VerifyBGPTimers, VerifyEVPNType2Route, @@ -4357,4 +4358,240 @@ ], }, }, + { + "name": "success", + "test": VerifyBGPRouteOrigin, + "eos_data": [ + { + "vrfs": { + "default": { + "bgpRouteEntries": { + "10.100.0.128/31": { + "bgpRoutePaths": [ + { + "nextHop": "10.100.0.10", + "routeDetail": { + "origin": "Igp", + }, + }, + { + "nextHop": "10.100.4.5", + "routeDetail": { + "origin": "Incomplete", + }, + }, + ], + } + } + } + } + }, + { + "vrfs": { + "MGMT": { + "bgpRouteEntries": { + "10.100.0.130/31": { + "bgpRoutePaths": [ + { + "nextHop": "10.100.0.8", + "routeDetail": { + "origin": "Igp", + }, + }, + { + "nextHop": "10.100.0.10", + "routeDetail": { + "origin": "Igp", + }, + }, + ], + } + } + } + } + }, + ], + "inputs": { + "route_entries": [ + { + "prefix": "10.100.0.128/31", + "vrf": "default", + "paths": [{"nexthop": "10.100.0.10", "origin": "Igp"}, {"nexthop": "10.100.4.5", "origin": "Incomplete"}], + }, + {"prefix": "10.100.0.130/31", "vrf": "MGMT", "paths": [{"nexthop": "10.100.0.8", "origin": "Igp"}, {"nexthop": "10.100.0.10", "origin": "Igp"}]}, + ] + }, + "expected": {"result": "success"}, + }, + { + "name": "failure-origin-not-correct", + "test": VerifyBGPRouteOrigin, + "eos_data": [ + { + "vrfs": { + "default": { + "bgpRouteEntries": { + "10.100.0.128/31": { + "bgpRoutePaths": [ + { + "nextHop": "10.100.0.10", + "routeDetail": { + "origin": "Igp", + }, + }, + { + "nextHop": "10.100.4.5", + "routeDetail": { + "origin": "Incomplete", + }, + }, + ], + } + } + } + } + }, + { + "vrfs": { + "MGMT": { + "bgpRouteEntries": { + "10.100.0.130/31": { + "bgpRoutePaths": [ + { + "nextHop": "10.100.0.8", + "routeDetail": { + "origin": "Igp", + }, + }, + { + "nextHop": "10.100.0.10", + "routeDetail": { + "origin": "Igp", + }, + }, + ], + } + } + } + } + }, + ], + "inputs": { + "route_entries": [ + { + "prefix": "10.100.0.128/31", + "vrf": "default", + "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + }, + { + "prefix": "10.100.0.130/31", + "vrf": "MGMT", + "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + }, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n" + "{'10.100.0.128/31': {'10.100.0.10': 'Expected `Incomplete` as the origin, but found `Igp` instead.', " + "'10.100.4.5': 'Expected `Igp` as the origin, but found `Incomplete` instead.'}, " + "'10.100.0.130/31': {'10.100.0.8': 'Expected `Incomplete` as the origin, but found `Igp` instead.', " + "'10.100.0.10': 'Expected `Incomplete` as the origin, but found `Igp` instead.'}}" + ], + }, + }, + { + "name": "failure-path-not-found", + "test": VerifyBGPRouteOrigin, + "eos_data": [ + { + "vrfs": { + "default": { + "bgpRouteEntries": { + "10.100.0.128/31": { + "bgpRoutePaths": [ + { + "nextHop": "10.100.0.15", + "routeDetail": { + "origin": "Igp", + }, + }, + ], + } + } + } + } + }, + { + "vrfs": { + "MGMT": { + "bgpRouteEntries": { + "10.100.0.130/31": { + "bgpRoutePaths": [ + { + "nextHop": "10.100.0.15", + "routeDetail": { + "origin": "Igp", + }, + }, + ], + } + } + } + } + }, + ], + "inputs": { + "route_entries": [ + { + "prefix": "10.100.0.128/31", + "vrf": "default", + "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + }, + { + "prefix": "10.100.0.130/31", + "vrf": "MGMT", + "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + }, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n" + "{'10.100.0.128/31': {'10.100.0.10': 'Path not found.', '10.100.4.5': 'Path not found.'}, " + "'10.100.0.130/31': {'10.100.0.8': 'Path not found.', '10.100.0.10': 'Path not found.'}}" + ], + }, + }, + { + "name": "failure-route-not-found", + "test": VerifyBGPRouteOrigin, + "eos_data": [ + {"vrfs": {"default": {"bgpRouteEntries": {}}}}, + {"vrfs": {"MGMT": {"bgpRouteEntries": {}}}}, + ], + "inputs": { + "route_entries": [ + { + "prefix": "10.100.0.128/31", + "vrf": "default", + "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + }, + { + "prefix": "10.100.0.130/31", + "vrf": "MGMT", + "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + }, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n" + "{'10.100.0.128/31': {'default': 'Not configured'}, '10.100.0.130/31': {'MGMT': 'Not configured'}}" + ], + }, + }, ] From 4046dfb016d42d2811fa237281c8883056c4a160 Mon Sep 17 00:00:00 2001 From: VitthalMagadum Date: Mon, 23 Sep 2024 07:07:22 -0400 Subject: [PATCH 2/9] issue_811 Handling review comments: updated the variable name and input schema --- anta/tests/routing/bgp.py | 19 ++++++++--------- examples/tests.yaml | 4 ++-- tests/units/anta_tests/routing/test_bgp.py | 24 +++++++++++++--------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 76c250d19..2c4735eac 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -1637,14 +1637,13 @@ class VerifyBGPRouteOrigin(AntaTest): bgp: - VerifyBGPRouteOrigin: route_entries: - - prefix: 10.100.0.128/31 - vrf: default - nexthop: 10.100.0.10 - origin: Igp - - prefix: 10.100.0.130/31 - vrf: default - nexthop: 10.100.0.8 - origin: Egp + - prefix: 10.100.0.128/31 + vrf: default + route_paths: + - nexthop: 10.100.0.10 + origin: Igp + - nexthop: 10.100.4.5 + origin: Incomplete ``` """ @@ -1666,7 +1665,7 @@ class BgpRoute(BaseModel): """IPv4 network address""" vrf: str = "default" """Optional VRF. If not provided, it defaults to `default`.""" - paths: list[dict[str, IPv4Address | Literal["Igp", "Egp", "Incomplete"]]] + route_paths: list[dict[str, IPv4Address | Literal["Igp", "Egp", "Incomplete"]]] """A list of dictionaries represents a BGP path. - `nexthop`: The next-hop IP address for the path. - `origin`: The origin of the route.""" @@ -1683,7 +1682,7 @@ def test(self) -> None: for command, input_entry in zip(self.instance_commands, self.inputs.route_entries): prefix = str(command.params.prefix) vrf = command.params.vrf - paths = input_entry.paths + paths = input_entry.route_paths # Verify if a BGP peer is configured with the provided vrf if not (bgp_routes := get_value(command.json_output, f"vrfs..{vrf}..bgpRouteEntries..{prefix}..bgpRoutePaths", separator="..")): diff --git a/examples/tests.yaml b/examples/tests.yaml index 27398850e..ca579c0d8 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -625,14 +625,14 @@ anta.tests.routing: route_entries: - prefix: 10.100.0.128/31 vrf: default - paths: + route_paths: - nexthop: 10.100.0.10 origin: Igp - nexthop: 10.100.4.5 origin: Incomplete - prefix: 10.100.0.130/31 vrf: default - paths: + route_paths: - nexthop: 10.100.0.8 origin: Igp - nexthop: 10.100.0.10 diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py index 8d24470a9..e9003b765 100644 --- a/tests/units/anta_tests/routing/test_bgp.py +++ b/tests/units/anta_tests/routing/test_bgp.py @@ -20,8 +20,8 @@ VerifyBGPPeerRouteRefreshCap, VerifyBGPPeersHealth, VerifyBGPPeerUpdateErrors, - VerifyBGPRouteOrigin, VerifyBgpRouteMaps, + VerifyBGPRouteOrigin, VerifyBGPSpecificPeers, VerifyBGPTimers, VerifyEVPNType2Route, @@ -4895,9 +4895,13 @@ { "prefix": "10.100.0.128/31", "vrf": "default", - "paths": [{"nexthop": "10.100.0.10", "origin": "Igp"}, {"nexthop": "10.100.4.5", "origin": "Incomplete"}], + "route_paths": [{"nexthop": "10.100.0.10", "origin": "Igp"}, {"nexthop": "10.100.4.5", "origin": "Incomplete"}], + }, + { + "prefix": "10.100.0.130/31", + "vrf": "MGMT", + "route_paths": [{"nexthop": "10.100.0.8", "origin": "Igp"}, {"nexthop": "10.100.0.10", "origin": "Igp"}], }, - {"prefix": "10.100.0.130/31", "vrf": "MGMT", "paths": [{"nexthop": "10.100.0.8", "origin": "Igp"}, {"nexthop": "10.100.0.10", "origin": "Igp"}]}, ] }, "expected": {"result": "success"}, @@ -4960,12 +4964,12 @@ { "prefix": "10.100.0.128/31", "vrf": "default", - "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + "route_paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], }, { "prefix": "10.100.0.130/31", "vrf": "MGMT", - "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + "route_paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], }, ] }, @@ -5026,12 +5030,12 @@ { "prefix": "10.100.0.128/31", "vrf": "default", - "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + "route_paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], }, { "prefix": "10.100.0.130/31", "vrf": "MGMT", - "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + "route_paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], }, ] }, @@ -5056,12 +5060,12 @@ { "prefix": "10.100.0.128/31", "vrf": "default", - "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + "route_paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], }, { "prefix": "10.100.0.130/31", "vrf": "MGMT", - "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + "route_paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], }, ] }, @@ -5073,4 +5077,4 @@ ], }, }, -] \ No newline at end of file +] From 1408772f325edf44fb88b84fd2655ba5d8346d40 Mon Sep 17 00:00:00 2001 From: VitthalMagadum Date: Wed, 25 Sep 2024 02:18:03 -0400 Subject: [PATCH 3/9] issue_811 Handling review comments: updated the pylint ignore and doctsring --- anta/tests/routing/bgp.py | 7 +++++-- examples/tests.yaml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 2c4735eac..5d866236c 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -18,6 +18,9 @@ from anta.models import AntaCommand, AntaTemplate, AntaTest from anta.tools import get_item, get_value +# pylint: disable=C0302 +# TODO: Refactor to reduce the number of lines in this module later + def _add_bgp_failures(failures: dict[tuple[str, str | None], dict[str, Any]], afi: Afi, safi: Safi | None, vrf: str, issue: str | dict[str, Any]) -> None: """Add a BGP failure entry to the given `failures` dictionary. @@ -1627,8 +1630,8 @@ class VerifyBGPRouteOrigin(AntaTest): Expected Results ---------------- - * Success: The test will pass if the BGP route's origin matches expected origin type. - * Failure: The test will fail if the BGP route's origin does not matches with expected origin type or BGP route entry(s) not found. + * Success: The test will pass if the BGP route's origin matches expected origin type and next-hop address. + * Failure: The test will fail if the BGP route's origin does not matches with expected origin type, next-hop address or BGP route entry(s) not found. Examples -------- diff --git a/examples/tests.yaml b/examples/tests.yaml index ca579c0d8..6b2b89c8c 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -694,4 +694,4 @@ anta.tests.routing: - endpoint: 1.0.0.14/32 vias: - type: ip - nexthop: 1.1.1.1 \ No newline at end of file + nexthop: 1.1.1.1 From b9d1bea501063d742d1591b3246030a3e708c63c Mon Sep 17 00:00:00 2001 From: VitthalMagadum Date: Fri, 27 Sep 2024 04:42:54 -0400 Subject: [PATCH 4/9] issue_811 fix lintin issue --- anta/tests/routing/bgp.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 41483b5de..6af2327eb 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -10,7 +10,6 @@ from ipaddress import IPv4Address, IPv4Network, IPv6Address from typing import TYPE_CHECKING, Any, ClassVar, Literal - from pydantic import BaseModel, Field, PositiveInt, model_validator from pydantic.v1.utils import deep_update from pydantic_extra_types.mac_address import MacAddress @@ -19,7 +18,6 @@ from anta.models import AntaCommand, AntaTemplate, AntaTest from anta.tools import get_item, get_value - if TYPE_CHECKING: import sys @@ -32,6 +30,7 @@ # pylint: disable=C0302 # TODO: Refactor to reduce the number of lines in this module later + def _add_bgp_failures(failures: dict[tuple[str, str | None], dict[str, Any]], afi: Afi, safi: Safi | None, vrf: str, issue: str | dict[str, Any]) -> None: """Add a BGP failure entry to the given `failures` dictionary. From 07d115fec390ea336bb0f4fa7f438b607b83c997 Mon Sep 17 00:00:00 2001 From: VitthalMagadum Date: Mon, 30 Sep 2024 05:22:46 -0400 Subject: [PATCH 5/9] issue_811 Handling review comments: updated vrf details in failure msg --- anta/tests/routing/bgp.py | 2 +- tests/units/anta_tests/routing/test_bgp.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 6af2327eb..8cf94a892 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -1715,7 +1715,7 @@ def test(self) -> None: # Updating failures. if failure: - failures[prefix] = failure + failures[prefix] = {vrf: failure} # Check if any failures if not failures: diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py index e9003b765..12cdebd83 100644 --- a/tests/units/anta_tests/routing/test_bgp.py +++ b/tests/units/anta_tests/routing/test_bgp.py @@ -4977,10 +4977,10 @@ "result": "failure", "messages": [ "Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n" - "{'10.100.0.128/31': {'10.100.0.10': 'Expected `Incomplete` as the origin, but found `Igp` instead.', " - "'10.100.4.5': 'Expected `Igp` as the origin, but found `Incomplete` instead.'}, " - "'10.100.0.130/31': {'10.100.0.8': 'Expected `Incomplete` as the origin, but found `Igp` instead.', " - "'10.100.0.10': 'Expected `Incomplete` as the origin, but found `Igp` instead.'}}" + "{'10.100.0.128/31': {'default': {'10.100.0.10': 'Expected `Incomplete` as the origin, but found `Igp` instead.', " + "'10.100.4.5': 'Expected `Igp` as the origin, but found `Incomplete` instead.'}}, " + "'10.100.0.130/31': {'MGMT': {'10.100.0.8': 'Expected `Incomplete` as the origin, but found `Igp` instead.', " + "'10.100.0.10': 'Expected `Incomplete` as the origin, but found `Igp` instead.'}}}" ], }, }, @@ -5043,8 +5043,8 @@ "result": "failure", "messages": [ "Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n" - "{'10.100.0.128/31': {'10.100.0.10': 'Path not found.', '10.100.4.5': 'Path not found.'}, " - "'10.100.0.130/31': {'10.100.0.8': 'Path not found.', '10.100.0.10': 'Path not found.'}}" + "{'10.100.0.128/31': {'default': {'10.100.0.10': 'Path not found.', '10.100.4.5': 'Path not found.'}}, " + "'10.100.0.130/31': {'MGMT': {'10.100.0.8': 'Path not found.', '10.100.0.10': 'Path not found.'}}}" ], }, }, From 15d5964b2d02e32e919371f44fc7a809d439894b Mon Sep 17 00:00:00 2001 From: VitthalMagadum Date: Thu, 10 Oct 2024 23:39:40 -0400 Subject: [PATCH 6/9] issue_811 Handling review comments: updated the input model for route path --- anta/tests/routing/bgp.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 8cf94a892..37d2be239 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -1677,10 +1677,16 @@ class BgpRoute(BaseModel): """IPv4 network address""" vrf: str = "default" """Optional VRF. If not provided, it defaults to `default`.""" - route_paths: list[dict[str, IPv4Address | Literal["Igp", "Egp", "Incomplete"]]] - """A list of dictionaries represents a BGP path. - - `nexthop`: The next-hop IP address for the path. - - `origin`: The origin of the route.""" + route_paths: list[RoutePath] + """List of BGP route path(s)""" + + class RoutePath(BaseModel): + """Model for a BGP route path(s).""" + + nexthop: IPv4Address + """The next-hop IPv4 address for the path.""" + origin: Literal["Igp", "Egp", "Incomplete"] + """The origin of the route.""" def render(self, template: AntaTemplate) -> list[AntaCommand]: """Render the template for each BGP peer in the input list.""" @@ -1704,8 +1710,8 @@ def test(self) -> None: # Iterating over each nexthop. failure: dict[Any, Any] = {} for path in paths: - nexthop = str(path["nexthop"]) - origin = path["origin"] + nexthop = str(path.nexthop) + origin = path.origin if not (route_path := get_item(bgp_routes, "nextHop", nexthop)): failure[nexthop] = "Path not found." continue From 224b1e77a37a2e26e4e5b3257d4a07fe9126123b Mon Sep 17 00:00:00 2001 From: vitthalmagadum Date: Wed, 18 Dec 2024 07:22:03 -0500 Subject: [PATCH 7/9] Added input models refactoring --- anta/input_models/routing/bgp.py | 51 ++++++++++++- anta/tests/routing/bgp.py | 84 ++++++++-------------- examples/tests.yaml | 10 +++ tests/units/anta_tests/routing/test_bgp.py | 64 +++++++---------- 4 files changed, 112 insertions(+), 97 deletions(-) diff --git a/anta/input_models/routing/bgp.py b/anta/input_models/routing/bgp.py index a291809c6..ac8f69368 100644 --- a/anta/input_models/routing/bgp.py +++ b/anta/input_models/routing/bgp.py @@ -5,8 +5,8 @@ from __future__ import annotations -from ipaddress import IPv4Address, IPv6Address -from typing import TYPE_CHECKING, Any +from ipaddress import IPv4Address, IPv4Network, IPv6Address +from typing import TYPE_CHECKING, Any, Literal from warnings import warn from pydantic import BaseModel, ConfigDict, PositiveInt, model_validator @@ -128,3 +128,50 @@ def __init__(self, **data: Any) -> None: # noqa: ANN401 stacklevel=2, ) super().__init__(**data) + + +class BgpRoute(BaseModel): + """Model representing BGP routes. + + Only IPv4 prefixes are supported for now. + """ + + model_config = ConfigDict(extra="forbid") + prefix: IPv4Network + """The IPv4 network address.""" + vrf: str = "default" + """Optional VRF for the BGP peer. Defaults to `default`.""" + paths: list[RoutePath] | None = None + """A list of paths for the BGP route. Required field in the `VerifyBGPRouteOrigin` test.""" + + def __str__(self) -> str: + """Return a human-readable string representation of the BgpRoute for reporting. + + Examples + -------- + - Prefix: 192.168.66.100/24 VRF: default + """ + return f"Prefix: {self.prefix} VRF: {self.vrf}" + + +class RoutePath(BaseModel): + """Model representing a BGP route path.""" + + model_config = ConfigDict(extra="forbid") + nexthop: IPv4Address + """The next-hop IPv4 address for the path.""" + origin: Literal["Igp", "Egp", "Incomplete"] + """The origin type of the BGP route path: + - 'Igp': Indicates the route originated from an interior gateway protocol (IGP). + - 'Egp': Indicates the route originated from an exterior gateway protocol (EGP). + - 'Incomplete': Indicates the origin is unknown or learned by other means. + """ + + def __str__(self) -> str: + """Return a human-readable string representation of the RoutePath for reporting. + + Examples + -------- + - Nexthop: 192.168.66.101 Origin: Igp + """ + return f"Nexthop: {self.nexthop}" diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 683d73a3c..994483833 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -7,15 +7,15 @@ # mypy: disable-error-code=attr-defined from __future__ import annotations -from ipaddress import IPv4Address, IPv4Network, IPv6Address -from typing import TYPE_CHECKING, Any, ClassVar, Literal +from ipaddress import IPv4Address, IPv4Network +from typing import TYPE_CHECKING, Any, ClassVar from pydantic import BaseModel, Field, field_validator, model_validator from pydantic.v1.utils import deep_update from pydantic_extra_types.mac_address import MacAddress from anta.custom_types import BgpDropStats, BgpUpdateError, MultiProtocolCaps, Vni -from anta.input_models.routing.bgp import BgpAddressFamily, BgpAfi +from anta.input_models.routing.bgp import BgpAddressFamily, BgpAfi, BgpRoute from anta.models import AntaCommand, AntaTemplate, AntaTest from anta.tools import format_data, get_item, get_value @@ -1398,12 +1398,23 @@ def test(self) -> None: class VerifyBGPRouteOrigin(AntaTest): - """Verifies BGP route origin for the provided IPv4 Network(s). + """Verifies BGP route origin. + + This test performs the following checks for each specified bgp route entry: + 1. Checks whether the specified BGP route entries exist. + 2. Confirms that the path exists and corresponds to the next-hop address. + 3. Verifies that the origin type of the BGP route matches the expected type. Expected Results ---------------- - * Success: The test will pass if the BGP route's origin matches expected origin type and next-hop address. - * Failure: The test will fail if the BGP route's origin does not matches with expected origin type, next-hop address or BGP route entry(s) not found. + * Success: The test will pass if: + - The BGP route entries exist for specified prefixes. + - Path exists and corresponds to the specified next-hop address. + - The origin type of the BGP route matches the expected type. + * Failure: The test will fail if: + - The BGP route entries does not exist for specified prefixes. + - The Path does not exists and corresponds to the specified next-hop address. + - The origin type does not match the configured value. Examples -------- @@ -1422,10 +1433,8 @@ class VerifyBGPRouteOrigin(AntaTest): ``` """ - name = "VerifyBGPRouteOrigin" - description = "Verifies BGP route origin for the provided IPv4 Network(s)." categories: ClassVar[list[str]] = ["bgp"] - commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaTemplate(template="show ip bgp {prefix} vrf {vrf}", revision=3)] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip bgp detail vrf all", revision=3)] class Input(AntaTest.Input): """Input model for the VerifyBGPRouteOrigin test.""" @@ -1433,61 +1442,26 @@ class Input(AntaTest.Input): route_entries: list[BgpRoute] """List of BGP route(s)""" - class BgpRoute(BaseModel): - """Model for a BGP route(s).""" - - prefix: IPv4Network - """IPv4 network address""" - vrf: str = "default" - """Optional VRF. If not provided, it defaults to `default`.""" - route_paths: list[RoutePath] - """List of BGP route path(s)""" - - class RoutePath(BaseModel): - """Model for a BGP route path(s).""" - - nexthop: IPv4Address - """The next-hop IPv4 address for the path.""" - origin: Literal["Igp", "Egp", "Incomplete"] - """The origin of the route.""" - - def render(self, template: AntaTemplate) -> list[AntaCommand]: - """Render the template for each BGP peer in the input list.""" - return [template.render(prefix=route.prefix, vrf=route.vrf) for route in self.inputs.route_entries] - @AntaTest.anta_test def test(self) -> None: """Main test function for VerifyBGPRouteOrigin.""" - failures: dict[Any, Any] = {} - - for command, input_entry in zip(self.instance_commands, self.inputs.route_entries): - prefix = str(command.params.prefix) - vrf = command.params.vrf - paths = input_entry.route_paths + self.result.is_success() - # Verify if a BGP peer is configured with the provided vrf - if not (bgp_routes := get_value(command.json_output, f"vrfs..{vrf}..bgpRouteEntries..{prefix}..bgpRoutePaths", separator="..")): - failures[prefix] = {vrf: "Not configured"} + for route in self.inputs.route_entries: + # Verify if a BGP routes are present with the provided vrf + if not ( + bgp_routes := get_value(self.instance_commands[0].json_output, f"vrfs..{route.vrf}..bgpRouteEntries..{route.prefix}..bgpRoutePaths", separator="..") + ): + self.result.is_failure(f"{route} - routes not found") continue - # Iterating over each nexthop. - failure: dict[Any, Any] = {} - for path in paths: + # Iterating over each path. + for path in route.paths: nexthop = str(path.nexthop) origin = path.origin if not (route_path := get_item(bgp_routes, "nextHop", nexthop)): - failure[nexthop] = "Path not found." + self.result.is_failure(f"{route} {path} - path not found") continue if (actual_origin := route_path.get("routeDetail", {}).get("origin", "Not Found")) != origin: - failure[nexthop] = f"Expected `{origin}` as the origin, but found `{actual_origin}` instead." - - # Updating failures. - if failure: - failures[prefix] = {vrf: failure} - - # Check if any failures - if not failures: - self.result.is_success() - else: - self.result.is_failure(f"Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n{failures}") + self.result.is_failure(f"{route} {path} - Origin mismatch - Expected: {origin} Actual: {actual_origin}") diff --git a/examples/tests.yaml b/examples/tests.yaml index d2a029986..390649db4 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -463,6 +463,16 @@ anta.tests.routing.bgp: safi: "unicast" vrf: "DEV" check_tcp_queues: false + - VerifyBGPRouteOrigin: + # Verifies BGP route origin. + route_entries: + - prefix: 10.100.0.128/31 + vrf: default + route_paths: + - nexthop: 10.100.0.10 + origin: Igp + - nexthop: 10.100.4.5 + origin: Incomplete - VerifyBGPSpecificPeers: # Verifies the health of specific BGP peer(s) for given address families. address_families: diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py index 34367b52e..2ba27832d 100644 --- a/tests/units/anta_tests/routing/test_bgp.py +++ b/tests/units/anta_tests/routing/test_bgp.py @@ -4522,11 +4522,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo ], } } - } - } - }, - { - "vrfs": { + }, "MGMT": { "bgpRouteEntries": { "10.100.0.130/31": { @@ -4546,7 +4542,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo ], } } - } + }, } }, ], @@ -4555,12 +4551,12 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo { "prefix": "10.100.0.128/31", "vrf": "default", - "route_paths": [{"nexthop": "10.100.0.10", "origin": "Igp"}, {"nexthop": "10.100.4.5", "origin": "Incomplete"}], + "paths": [{"nexthop": "10.100.0.10", "origin": "Igp"}, {"nexthop": "10.100.4.5", "origin": "Incomplete"}], }, { "prefix": "10.100.0.130/31", "vrf": "MGMT", - "route_paths": [{"nexthop": "10.100.0.8", "origin": "Igp"}, {"nexthop": "10.100.0.10", "origin": "Igp"}], + "paths": [{"nexthop": "10.100.0.8", "origin": "Igp"}, {"nexthop": "10.100.0.10", "origin": "Igp"}], }, ] }, @@ -4591,11 +4587,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo ], } } - } - } - }, - { - "vrfs": { + }, "MGMT": { "bgpRouteEntries": { "10.100.0.130/31": { @@ -4615,7 +4607,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo ], } } - } + }, } }, ], @@ -4624,23 +4616,22 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo { "prefix": "10.100.0.128/31", "vrf": "default", - "route_paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], }, { "prefix": "10.100.0.130/31", "vrf": "MGMT", - "route_paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], }, ] }, "expected": { "result": "failure", "messages": [ - "Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n" - "{'10.100.0.128/31': {'default': {'10.100.0.10': 'Expected `Incomplete` as the origin, but found `Igp` instead.', " - "'10.100.4.5': 'Expected `Igp` as the origin, but found `Incomplete` instead.'}}, " - "'10.100.0.130/31': {'MGMT': {'10.100.0.8': 'Expected `Incomplete` as the origin, but found `Igp` instead.', " - "'10.100.0.10': 'Expected `Incomplete` as the origin, but found `Igp` instead.'}}}" + "Prefix: 10.100.0.128/31 VRF: default Nexthop: 10.100.0.10 - Origin mismatch - Expected: Incomplete Actual: Igp", + "Prefix: 10.100.0.128/31 VRF: default Nexthop: 10.100.4.5 - Origin mismatch - Expected: Igp Actual: Incomplete", + "Prefix: 10.100.0.130/31 VRF: MGMT Nexthop: 10.100.0.8 - Origin mismatch - Expected: Incomplete Actual: Igp", + "Prefix: 10.100.0.130/31 VRF: MGMT Nexthop: 10.100.0.10 - Origin mismatch - Expected: Incomplete Actual: Igp", ], }, }, @@ -4663,11 +4654,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo ], } } - } - } - }, - { - "vrfs": { + }, "MGMT": { "bgpRouteEntries": { "10.100.0.130/31": { @@ -4681,7 +4668,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo ], } } - } + }, } }, ], @@ -4690,21 +4677,22 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo { "prefix": "10.100.0.128/31", "vrf": "default", - "route_paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], }, { "prefix": "10.100.0.130/31", "vrf": "MGMT", - "route_paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], }, ] }, "expected": { "result": "failure", "messages": [ - "Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n" - "{'10.100.0.128/31': {'default': {'10.100.0.10': 'Path not found.', '10.100.4.5': 'Path not found.'}}, " - "'10.100.0.130/31': {'MGMT': {'10.100.0.8': 'Path not found.', '10.100.0.10': 'Path not found.'}}}" + "Prefix: 10.100.0.128/31 VRF: default Nexthop: 10.100.0.10 - path not found", + "Prefix: 10.100.0.128/31 VRF: default Nexthop: 10.100.4.5 - path not found", + "Prefix: 10.100.0.130/31 VRF: MGMT Nexthop: 10.100.0.8 - path not found", + "Prefix: 10.100.0.130/31 VRF: MGMT Nexthop: 10.100.0.10 - path not found", ], }, }, @@ -4712,29 +4700,25 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "name": "failure-route-not-found", "test": VerifyBGPRouteOrigin, "eos_data": [ - {"vrfs": {"default": {"bgpRouteEntries": {}}}}, - {"vrfs": {"MGMT": {"bgpRouteEntries": {}}}}, + {"vrfs": {"default": {"bgpRouteEntries": {}}, "MGMT": {"bgpRouteEntries": {}}}}, ], "inputs": { "route_entries": [ { "prefix": "10.100.0.128/31", "vrf": "default", - "route_paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], + "paths": [{"nexthop": "10.100.0.10", "origin": "Incomplete"}, {"nexthop": "10.100.4.5", "origin": "Igp"}], }, { "prefix": "10.100.0.130/31", "vrf": "MGMT", - "route_paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], + "paths": [{"nexthop": "10.100.0.8", "origin": "Incomplete"}, {"nexthop": "10.100.0.10", "origin": "Incomplete"}], }, ] }, "expected": { "result": "failure", - "messages": [ - "Following BGP route entry(s) or nexthop path(s) not found or origin type is not correct:\n" - "{'10.100.0.128/31': {'default': 'Not configured'}, '10.100.0.130/31': {'MGMT': 'Not configured'}}" - ], + "messages": ["Prefix: 10.100.0.128/31 VRF: default - routes not found", "Prefix: 10.100.0.130/31 VRF: MGMT - routes not found"], }, }, ] From b742646b9a829a13fb5bbb064d144266a50d62a1 Mon Sep 17 00:00:00 2001 From: vitthalmagadum Date: Thu, 19 Dec 2024 07:00:50 -0500 Subject: [PATCH 8/9] Addressed review comment: updated doc, unit test eos data --- anta/tests/routing/bgp.py | 12 ++++++------ examples/tests.yaml | 2 +- tests/units/anta_tests/routing/test_bgp.py | 20 ++++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 994483833..0a9392e5b 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -1401,19 +1401,19 @@ class VerifyBGPRouteOrigin(AntaTest): """Verifies BGP route origin. This test performs the following checks for each specified bgp route entry: - 1. Checks whether the specified BGP route entries exist. - 2. Confirms that the path exists and corresponds to the next-hop address. + 1. Checks whether the specified BGP route entry exists. + 2. Confirms that each path for the route entry exists and corresponds to the next-hop address. 3. Verifies that the origin type of the BGP route matches the expected type. Expected Results ---------------- * Success: The test will pass if: - The BGP route entries exist for specified prefixes. - - Path exists and corresponds to the specified next-hop address. + - Every path exists and corresponds to the specified next-hop address. - The origin type of the BGP route matches the expected type. * Failure: The test will fail if: - The BGP route entries does not exist for specified prefixes. - - The Path does not exists and corresponds to the specified next-hop address. + - Any Path does not exists and corresponds to the specified next-hop address. - The origin type does not match the configured value. Examples @@ -1425,7 +1425,7 @@ class VerifyBGPRouteOrigin(AntaTest): route_entries: - prefix: 10.100.0.128/31 vrf: default - route_paths: + paths: - nexthop: 10.100.0.10 origin: Igp - nexthop: 10.100.4.5 @@ -1463,5 +1463,5 @@ def test(self) -> None: self.result.is_failure(f"{route} {path} - path not found") continue - if (actual_origin := route_path.get("routeDetail", {}).get("origin", "Not Found")) != origin: + if (actual_origin := route_path.get("routeType", {}).get("origin", "Not Found")) != origin: self.result.is_failure(f"{route} {path} - Origin mismatch - Expected: {origin} Actual: {actual_origin}") diff --git a/examples/tests.yaml b/examples/tests.yaml index 390649db4..bfa6def51 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -468,7 +468,7 @@ anta.tests.routing.bgp: route_entries: - prefix: 10.100.0.128/31 vrf: default - route_paths: + paths: - nexthop: 10.100.0.10 origin: Igp - nexthop: 10.100.4.5 diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py index 2ba27832d..ae398173f 100644 --- a/tests/units/anta_tests/routing/test_bgp.py +++ b/tests/units/anta_tests/routing/test_bgp.py @@ -4509,13 +4509,13 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "bgpRoutePaths": [ { "nextHop": "10.100.0.10", - "routeDetail": { + "routeType": { "origin": "Igp", }, }, { "nextHop": "10.100.4.5", - "routeDetail": { + "routeType": { "origin": "Incomplete", }, }, @@ -4529,13 +4529,13 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "bgpRoutePaths": [ { "nextHop": "10.100.0.8", - "routeDetail": { + "routeType": { "origin": "Igp", }, }, { "nextHop": "10.100.0.10", - "routeDetail": { + "routeType": { "origin": "Igp", }, }, @@ -4574,13 +4574,13 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "bgpRoutePaths": [ { "nextHop": "10.100.0.10", - "routeDetail": { + "routeType": { "origin": "Igp", }, }, { "nextHop": "10.100.4.5", - "routeDetail": { + "routeType": { "origin": "Incomplete", }, }, @@ -4594,13 +4594,13 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "bgpRoutePaths": [ { "nextHop": "10.100.0.8", - "routeDetail": { + "routeType": { "origin": "Igp", }, }, { "nextHop": "10.100.0.10", - "routeDetail": { + "routeType": { "origin": "Igp", }, }, @@ -4647,7 +4647,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "bgpRoutePaths": [ { "nextHop": "10.100.0.15", - "routeDetail": { + "routeType": { "origin": "Igp", }, }, @@ -4661,7 +4661,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "bgpRoutePaths": [ { "nextHop": "10.100.0.15", - "routeDetail": { + "routeType": { "origin": "Igp", }, }, From 3981dd281cd57ea555ace0385eb867c1f0756d40 Mon Sep 17 00:00:00 2001 From: vitthalmagadum Date: Mon, 30 Dec 2024 11:36:28 -0500 Subject: [PATCH 9/9] pre-commit changes after conflicts resolved --- anta/input_models/routing/bgp.py | 5 ++++- anta/tests/routing/bgp.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/anta/input_models/routing/bgp.py b/anta/input_models/routing/bgp.py index 067d72bc9..45d25868c 100644 --- a/anta/input_models/routing/bgp.py +++ b/anta/input_models/routing/bgp.py @@ -207,10 +207,11 @@ class VxlanEndpoint(BaseModel): def __str__(self) -> str: """Return a human-readable string representation of the VxlanEndpoint for reporting.""" return f"Address: {self.address} VNI: {self.vni}" - + class BgpRoute(BaseModel): """Model representing BGP routes. + Only IPv4 prefixes are supported for now. """ @@ -224,6 +225,7 @@ class BgpRoute(BaseModel): def __str__(self) -> str: """Return a human-readable string representation of the BgpRoute for reporting. + Examples -------- - Prefix: 192.168.66.100/24 VRF: default @@ -246,6 +248,7 @@ class RoutePath(BaseModel): def __str__(self) -> str: """Return a human-readable string representation of the RoutePath for reporting. + Examples -------- - Nexthop: 192.168.66.101 Origin: Igp diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 667a82061..081e23445 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -11,7 +11,7 @@ from pydantic import field_validator -from anta.input_models.routing.bgp import BgpAddressFamily, BgpAfi, BgpNeighbor, BgpPeer, VxlanEndpoint, BgpRoute +from anta.input_models.routing.bgp import BgpAddressFamily, BgpAfi, BgpNeighbor, BgpPeer, BgpRoute, VxlanEndpoint from anta.models import AntaCommand, AntaTemplate, AntaTest from anta.tools import format_data, get_item, get_value