diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 344330d2c..97f919876 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -1534,3 +1534,89 @@ def test(self) -> None: self.result.is_failure( f"The following BGP peers are not configured or has an incorrect or missing route map in either the inbound or outbound direction:\n{failures}" ) + + +class VerifyBGPPeerRouteLimit(AntaTest): + """Verifies the maximum routes and optionally verifies the maximum routes warning limit for the provided BGP IPv4 peer(s). + + Expected Results + ---------------- + * Success: The test will pass if the BGP peer's maximum routes and, if provided, the maximum routes warning limit are equal to the given limits. + * Failure: The test will fail if the BGP peer's maximum routes do not match the given limit, or if the maximum routes warning limit is provided + and does not match the given limit, or if the peer is not configured. + + Examples + -------- + ```yaml + anta.tests.routing: + bgp: + - VerifyBGPPeerRouteLimit: + bgp_peers: + - peer_address: 172.30.11.1 + vrf: default + maximum_routes: 12000 + warning_limit: 10000 + ``` + """ + + name = "VerifyBGPPeerRouteLimit" + description = "Verifies maximum routes and maximum routes warning limit for the provided BGP IPv4 peer(s)." + categories: ClassVar[list[str]] = ["bgp"] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaTemplate(template="show bgp neighbors {peer} vrf {vrf}", revision=3)] + + class Input(AntaTest.Input): + """Input model for the VerifyBGPPeerRouteLimit test.""" + + bgp_peers: list[BgpPeer] + """List of BGP peers""" + + class BgpPeer(BaseModel): + """Model for a BGP peer.""" + + peer_address: IPv4Address + """IPv4 address of a BGP peer.""" + vrf: str = "default" + """Optional VRF for BGP peer. If not provided, it defaults to `default`.""" + maximum_routes: int = Field(ge=0, le=4294967294) + """The maximum allowable number of BGP routes, `0` means unlimited.""" + warning_limit: int = Field(default=0, ge=0, le=4294967294) + """Optional maximum routes warning limit. If not provided, it defaults to `0` meaning no warning limit.""" + + def render(self, template: AntaTemplate) -> list[AntaCommand]: + """Render the template for each BGP peer in the input list.""" + return [template.render(peer=str(bgp_peer.peer_address), vrf=bgp_peer.vrf) for bgp_peer in self.inputs.bgp_peers] + + @AntaTest.anta_test + def test(self) -> None: + """Main test function for VerifyBGPPeerRouteLimit.""" + failures: dict[Any, Any] = {} + + for command, input_entry in zip(self.instance_commands, self.inputs.bgp_peers): + peer = str(input_entry.peer_address) + vrf = input_entry.vrf + maximum_routes = input_entry.maximum_routes + warning_limit = input_entry.warning_limit + failure: dict[Any, Any] = {} + + # Verify BGP peer. + if not (peer_list := get_value(command.json_output, f"vrfs.{vrf}.peerList")) or (peer_detail := get_item(peer_list, "peerAddress", peer)) is None: + failures[peer] = {vrf: "Not configured"} + continue + + # Verify maximum routes configured. + if (actual_routes := peer_detail.get("maxTotalRoutes", "Not Found")) != maximum_routes: + failure["Maximum total routes"] = actual_routes + + # Verify warning limit if given. + if warning_limit and (actual_warning_limit := peer_detail.get("totalRoutesWarnLimit", "Not Found")) != warning_limit: + failure["Warning limit"] = actual_warning_limit + + # Updated failures if any. + if failure: + failures[peer] = {vrf: failure} + + # Check if any failures + if not failures: + self.result.is_success() + else: + self.result.is_failure(f"The following BGP peer(s) are not configured or maximum routes and maximum routes warning limit is not correct:\n{failures}") diff --git a/examples/tests.yaml b/examples/tests.yaml index 1c8f223d7..954b5b736 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -615,6 +615,12 @@ anta.tests.routing: vrf: default inbound_route_map: RM-MLAG-PEER-IN outbound_route_map: RM-MLAG-PEER-IN + - VerifyBGPPeerRouteLimit: + bgp_peers: + - peer_address: 10.100.0.8 + vrf: default + maximum_routes: 12000 + warning_limit: 10000 ospf: - VerifyOSPFNeighborState: - VerifyOSPFNeighborCount: diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py index 9948a53be..ae306cdff 100644 --- a/tests/units/anta_tests/routing/test_bgp.py +++ b/tests/units/anta_tests/routing/test_bgp.py @@ -18,6 +18,7 @@ VerifyBGPPeerDropStats, VerifyBGPPeerMD5Auth, VerifyBGPPeerMPCaps, + VerifyBGPPeerRouteLimit, VerifyBGPPeerRouteRefreshCap, VerifyBGPPeersHealth, VerifyBGPPeerUpdateErrors, @@ -4705,4 +4706,161 @@ ], }, }, + { + "name": "success", + "test": VerifyBGPPeerRouteLimit, + "eos_data": [ + { + "vrfs": { + "default": { + "peerList": [ + { + "peerAddress": "10.100.0.8", + "maxTotalRoutes": 12000, + "totalRoutesWarnLimit": 10000, + } + ] + }, + }, + }, + { + "vrfs": { + "MGMT": { + "peerList": [ + { + "peerAddress": "10.100.0.9", + "maxTotalRoutes": 10000, + "totalRoutesWarnLimit": 9000, + } + ] + }, + }, + }, + ], + "inputs": { + "bgp_peers": [ + {"peer_address": "10.100.0.8", "vrf": "default", "maximum_routes": 12000, "warning_limit": 10000}, + {"peer_address": "10.100.0.9", "vrf": "MGMT", "maximum_routes": 10000}, + ] + }, + "expected": {"result": "success"}, + }, + { + "name": "failure-peer-not-found", + "test": VerifyBGPPeerRouteLimit, + "eos_data": [ + { + "vrfs": { + "default": {}, + }, + }, + { + "vrfs": { + "MGMT": {}, + }, + }, + ], + "inputs": { + "bgp_peers": [ + {"peer_address": "10.100.0.8", "vrf": "default", "maximum_routes": 12000, "warning_limit": 10000}, + {"peer_address": "10.100.0.9", "vrf": "MGMT", "maximum_routes": 10000, "warning_limit": 9000}, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "The following BGP peer(s) are not configured or maximum routes and maximum routes warning limit is not correct:\n" + "{'10.100.0.8': {'default': 'Not configured'}, '10.100.0.9': {'MGMT': 'Not configured'}}" + ], + }, + }, + { + "name": "failure-incorrect-max-routes", + "test": VerifyBGPPeerRouteLimit, + "eos_data": [ + { + "vrfs": { + "default": { + "peerList": [ + { + "peerAddress": "10.100.0.8", + "maxTotalRoutes": 13000, + "totalRoutesWarnLimit": 11000, + } + ] + }, + }, + }, + { + "vrfs": { + "MGMT": { + "peerList": [ + { + "peerAddress": "10.100.0.9", + "maxTotalRoutes": 11000, + "totalRoutesWarnLimit": 10000, + } + ] + }, + }, + }, + ], + "inputs": { + "bgp_peers": [ + {"peer_address": "10.100.0.8", "vrf": "default", "maximum_routes": 12000, "warning_limit": 10000}, + {"peer_address": "10.100.0.9", "vrf": "MGMT", "maximum_routes": 10000, "warning_limit": 9000}, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "The following BGP peer(s) are not configured or maximum routes and maximum routes warning limit is not correct:\n" + "{'10.100.0.8': {'default': {'Maximum total routes': 13000, 'Warning limit': 11000}}, " + "'10.100.0.9': {'MGMT': {'Maximum total routes': 11000, 'Warning limit': 10000}}}" + ], + }, + }, + { + "name": "failure-routes-not-found", + "test": VerifyBGPPeerRouteLimit, + "eos_data": [ + { + "vrfs": { + "default": { + "peerList": [ + { + "peerAddress": "10.100.0.8", + "maxTotalRoutes": 12000, + } + ] + }, + }, + }, + { + "vrfs": { + "MGMT": { + "peerList": [ + { + "peerAddress": "10.100.0.9", + } + ] + }, + }, + }, + ], + "inputs": { + "bgp_peers": [ + {"peer_address": "10.100.0.8", "vrf": "default", "maximum_routes": 12000, "warning_limit": 10000}, + {"peer_address": "10.100.0.9", "vrf": "MGMT", "maximum_routes": 10000, "warning_limit": 9000}, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "The following BGP peer(s) are not configured or maximum routes and maximum routes warning limit is not correct:\n" + "{'10.100.0.8': {'default': {'Warning limit': 'Not Found'}}, " + "'10.100.0.9': {'MGMT': {'Maximum total routes': 'Not Found', 'Warning limit': 'Not Found'}}}" + ], + }, + }, ]