diff --git a/anta/input_models/routing/bgp.py b/anta/input_models/routing/bgp.py index 57c821740..4a99834b1 100644 --- a/anta/input_models/routing/bgp.py +++ b/anta/input_models/routing/bgp.py @@ -169,9 +169,11 @@ class BgpPeer(BaseModel): outbound_route_map: str | None = None """Outbound route map applied, defaults to None. Required field in the `VerifyBgpRouteMaps` test.""" maximum_routes: int | None = Field(default=None, ge=0, le=4294967294) - """The maximum allowable number of BGP routes, `0` means unlimited. Required field in the `VerifyBGPPeerRouteLimit` test""" + """The maximum allowable number of BGP routes. `0` means unlimited. Required field in the `VerifyBGPPeerRouteLimit` test""" warning_limit: int | None = Field(default=None, ge=0, le=4294967294) - """Optional maximum routes warning limit. If not provided, it defaults to `0` meaning no warning limit.""" + """The warning limit for the maximum routes. `0` means no warning. + + Optional field in the `VerifyBGPPeerRouteLimit` test. If not provided, the test will not verify the warning limit.""" def __str__(self) -> str: """Return a human-readable string representation of the BgpPeer for reporting.""" diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 2a140ddb2..7f3b3853a 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -331,7 +331,7 @@ def test(self) -> None: class VerifyBGPExchangedRoutes(AntaTest): - """Verifies the advertised and received routes of BGP peers. + """Verifies the advertised and received routes of BGP IPv4 peer(s). This test performs the following checks for each specified peer: @@ -387,7 +387,7 @@ class Input(AntaTest.Input): @field_validator("bgp_peers") @classmethod def validate_bgp_peers(cls, bgp_peers: list[BgpPeer]) -> list[BgpPeer]: - """Validate that 'advertised_routes' or 'received_routes' field is provided in each address family.""" + """Validate that 'advertised_routes' or 'received_routes' field is provided in each BGP peer.""" for peer in bgp_peers: if peer.advertised_routes is None or peer.received_routes is None: msg = f"{peer} 'advertised_routes' or 'received_routes' field missing in the input" @@ -435,7 +435,7 @@ def test(self) -> None: class VerifyBGPPeerMPCaps(AntaTest): - """Verifies the multiprotocol capabilities of BGP peers. + """Verifies the multiprotocol capabilities of BGP IPv4 peer(s). This test performs the following checks for each specified peer: @@ -490,7 +490,7 @@ class Input(AntaTest.Input): @field_validator("bgp_peers") @classmethod def validate_bgp_peers(cls, bgp_peers: list[T]) -> list[T]: - """Validate that 'capabilities' field is provided in each address family.""" + """Validate that 'capabilities' field is provided in each BGP peer.""" for peer in bgp_peers: if peer.capabilities is None: msg = f"{peer} 'capabilities' field missing in the input" @@ -537,7 +537,7 @@ def test(self) -> None: class VerifyBGPPeerASNCap(AntaTest): - """Verifies the four octet ASN capability of BGP peers. + """Verifies the four octet ASN capability of BGP IPv4 peer(s). This test performs the following checks for each specified peer: @@ -606,7 +606,7 @@ def test(self) -> None: class VerifyBGPPeerRouteRefreshCap(AntaTest): - """Verifies the route refresh capabilities of a BGP peer in a specified VRF. + """Verifies the route refresh capabilities of IPv4 BGP peer(s) in a specified VRF. This test performs the following checks for each specified peer: @@ -675,7 +675,7 @@ def test(self) -> None: class VerifyBGPPeerMD5Auth(AntaTest): - """Verifies the MD5 authentication and state of IPv4 BGP peers in a specified VRF. + """Verifies the MD5 authentication and state of IPv4 BGP peer(s) in a specified VRF. This test performs the following checks for each specified peer: @@ -814,7 +814,7 @@ def test(self) -> None: class VerifyBGPAdvCommunities(AntaTest): - """Verifies that advertised communities are standard, extended and large for BGP peers. + """Verifies that advertised communities are standard, extended and large for BGP IPv4 peer(s). This test performs the following checks for each specified peer: @@ -880,7 +880,7 @@ def test(self) -> None: class VerifyBGPTimers(AntaTest): - """Verifies the timers of BGP peers. + """Verifies the timers of BGP IPv4 peer(s). This test performs the following checks for each specified peer: @@ -928,7 +928,7 @@ class Input(AntaTest.Input): @field_validator("bgp_peers") @classmethod def validate_bgp_peers(cls, bgp_peers: list[T]) -> list[T]: - """Validate that 'hold_time' or 'keep_alive_time' field is provided in each address family.""" + """Validate that 'hold_time' or 'keep_alive_time' field is provided in each BGP peer.""" for peer in bgp_peers: if peer.hold_time is None or peer.keep_alive_time is None: msg = f"{peer} 'hold_time' or 'keep_alive_time' field missing in the input" @@ -1158,10 +1158,7 @@ class Input(AntaTest.Input): @field_validator("bgp_peers") @classmethod def validate_bgp_peers(cls, bgp_peers: list[T]) -> list[T]: - """Validate that 'peers' field is provided in each address family. - - At least one of 'inbound' or 'outbound' route-map must be provided. - """ + """Validate that 'inbound_route_map' or 'outbound_route_map' field is provided in each BGP peer.""" for peer in bgp_peers: if not (peer.inbound_route_map or peer.outbound_route_map): msg = f"{peer}; At least one of 'inbound_route_map' or 'outbound_route_map' must be provided." @@ -1196,22 +1193,22 @@ def test(self) -> None: class VerifyBGPPeerRouteLimit(AntaTest): - """Verifies maximum routes and outbound route-maps of BGP IPv4 peer(s). + """Verifies maximum routes and warning limit for BGP IPv4 peer(s). This test performs the following checks for each specified peer: 1. Confirms that the specified VRF is configured. 2. Verifies that the peer exists in the BGP configuration. - 3. Confirms the Maximum routes and maximum routes warning limit, if provided match the expected value. + 3. Confirms the maximum routes and maximum routes warning limit, if provided, match the expected value. Expected Results ---------------- * Success: If all of the following conditions are met: - All specified peers are found in the BGP configuration. - - The maximum routese/maximum routes warning limit match the expected value for a peer. + - The maximum routes/maximum routes warning limit match the expected value for a peer. * Failure: If any of the following occur: - A specified peer is not found in the BGP configuration. - - The maximum routese/maximum routes warning limit do not match the expected value for a peer. + - The maximum routes/maximum routes warning limit do not match the expected value for a peer. Examples -------- @@ -1240,7 +1237,7 @@ class Input(AntaTest.Input): @field_validator("bgp_peers") @classmethod def validate_bgp_peers(cls, bgp_peers: list[T]) -> list[T]: - """Validate that 'peers' field is provided in each address family.""" + """Validate that 'maximum_routes' field is provided in each BGP peer.""" for peer in bgp_peers: if peer.maximum_routes is None: msg = f"{peer}; 'maximum_routes' field missing in the input" @@ -1265,10 +1262,10 @@ def test(self) -> None: self.result.is_failure(f"{peer} - Not found") continue - # Verify maximum routes configured. - if (actual_routes := peer_data.get("maxTotalRoutes", "Not Found")) != maximum_routes: - self.result.is_failure(f"{peer} - Maximum routes mismatch - Expected: {maximum_routes}, Actual: {actual_routes}") + # Verify maximum routes + if (actual_maximum_routes := peer_data.get("maxTotalRoutes", "Not Found")) != maximum_routes: + self.result.is_failure(f"{peer} - Maximum routes mismatch - Expected: {maximum_routes}, Actual: {actual_maximum_routes}") - # Verify warning limit if given. - if warning_limit and (actual_warning_limit := peer_data.get("totalRoutesWarnLimit", "Not Found")) != warning_limit: - self.result.is_failure(f"{peer} - Maximum route warning limit mismatch - Expected: {warning_limit}, Actual: {actual_warning_limit}") + # Verify warning limit if provided. By default, EOS does not have a warning limit and `totalRoutesWarnLimit` is not present in the output. + if warning_limit is not None and (actual_warning_limit := peer_data.get("totalRoutesWarnLimit", 0)) != warning_limit: + self.result.is_failure(f"{peer} - Maximum routes warning limit mismatch - Expected: {warning_limit}, Actual: {actual_warning_limit}") diff --git a/examples/tests.yaml b/examples/tests.yaml index e22acf49a..92e8b00cc 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -366,14 +366,14 @@ anta.tests.ptp: # Verifies the PTP interfaces state. anta.tests.routing.bgp: - VerifyBGPAdvCommunities: - # Verifies that advertised communities are standard, extended and large for BGP peers. + # Verifies that advertised communities are standard, extended and large for BGP IPv4 peer(s). bgp_peers: - peer_address: 172.30.11.17 vrf: default - peer_address: 172.30.11.21 vrf: default - VerifyBGPExchangedRoutes: - # Verifies the advertised and received routes of BGP peers. + # Verifies the advertised and received routes of BGP IPv4 peer(s). bgp_peers: - peer_address: 172.30.255.5 vrf: default @@ -389,7 +389,7 @@ anta.tests.routing.bgp: received_routes: - 192.0.254.3/32 - VerifyBGPPeerASNCap: - # Verifies the four octet ASN capability of BGP peers. + # Verifies the four octet ASN capability of BGP IPv4 peer(s). bgp_peers: - peer_address: 172.30.11.1 vrf: default @@ -419,14 +419,14 @@ anta.tests.routing.bgp: - inDropAsloop - prefixEvpnDroppedUnsupportedRouteType - VerifyBGPPeerMD5Auth: - # Verifies the MD5 authentication and state of IPv4 BGP peers in a specified VRF. + # Verifies the MD5 authentication and state of IPv4 BGP peer(s) in a specified VRF. bgp_peers: - peer_address: 172.30.11.1 vrf: default - peer_address: 172.30.11.5 vrf: default - VerifyBGPPeerMPCaps: - # Verifies the multiprotocol capabilities of BGP peers. + # Verifies the multiprotocol capabilities of BGP IPv4 peer(s). bgp_peers: - peer_address: 172.30.11.1 vrf: default @@ -434,14 +434,14 @@ anta.tests.routing.bgp: capabilities: - ipv4Unicast - VerifyBGPPeerRouteLimit: - # Verifies maximum routes and outbound route-maps of BGP IPv4 peer(s). + # Verifies maximum routes and warning limit for BGP IPv4 peer(s). bgp_peers: - peer_address: 172.30.11.1 vrf: default maximum_routes: 12000 warning_limit: 10000 - VerifyBGPPeerRouteRefreshCap: - # Verifies the route refresh capabilities of a BGP peer in a specified VRF. + # Verifies the route refresh capabilities of IPv4 BGP peer(s) in a specified VRF. bgp_peers: - peer_address: 172.30.11.1 vrf: default @@ -478,7 +478,7 @@ anta.tests.routing.bgp: - 10.1.255.2 - 10.1.255.4 - VerifyBGPTimers: - # Verifies the timers of BGP peers. + # Verifies the timers of BGP IPv4 peer(s). bgp_peers: - peer_address: 172.30.11.1 vrf: default diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py index 59a67191c..fb454f3c2 100644 --- a/tests/units/anta_tests/routing/test_bgp.py +++ b/tests/units/anta_tests/routing/test_bgp.py @@ -3740,6 +3740,39 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo }, "expected": {"result": "success"}, }, + { + "name": "success-no-warning-limit", + "test": VerifyBGPPeerRouteLimit, + "eos_data": [ + { + "vrfs": { + "default": { + "peerList": [ + { + "peerAddress": "10.100.0.8", + "maxTotalRoutes": 12000, + } + ] + }, + "MGMT": { + "peerList": [ + { + "peerAddress": "10.100.0.9", + "maxTotalRoutes": 10000, + } + ] + }, + }, + }, + ], + "inputs": { + "bgp_peers": [ + {"peer_address": "10.100.0.8", "vrf": "default", "maximum_routes": 12000, "warning_limit": 0}, + {"peer_address": "10.100.0.9", "vrf": "MGMT", "maximum_routes": 10000}, + ] + }, + "expected": {"result": "success"}, + }, { "name": "failure-peer-not-found", "test": VerifyBGPPeerRouteLimit, @@ -3802,9 +3835,9 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "result": "failure", "messages": [ "Peer: 10.100.0.8 VRF: default - Maximum routes mismatch - Expected: 12000, Actual: 13000", - "Peer: 10.100.0.8 VRF: default - Maximum route warning limit mismatch - Expected: 10000, Actual: 11000", + "Peer: 10.100.0.8 VRF: default - Maximum routes warning limit mismatch - Expected: 10000, Actual: 11000", "Peer: 10.100.0.9 VRF: MGMT - Maximum routes mismatch - Expected: 10000, Actual: 11000", - "Peer: 10.100.0.9 VRF: MGMT - Maximum route warning limit mismatch - Expected: 9000, Actual: 10000", + "Peer: 10.100.0.9 VRF: MGMT - Maximum routes warning limit mismatch - Expected: 9000, Actual: 10000", ], }, }, @@ -3826,6 +3859,7 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "peerList": [ { "peerAddress": "10.100.0.9", + "maxTotalRoutes": 10000, } ] }, @@ -3841,9 +3875,8 @@ def test_check_bgp_neighbor_capability(input_dict: dict[str, bool], expected: bo "expected": { "result": "failure", "messages": [ - "Peer: 10.100.0.8 VRF: default - Maximum route warning limit mismatch - Expected: 10000, Actual: Not Found", - "Peer: 10.100.0.9 VRF: MGMT - Maximum routes mismatch - Expected: 10000, Actual: Not Found", - "Peer: 10.100.0.9 VRF: MGMT - Maximum route warning limit mismatch - Expected: 9000, Actual: Not Found", + "Peer: 10.100.0.8 VRF: default - Maximum routes warning limit mismatch - Expected: 10000, Actual: 0", + "Peer: 10.100.0.9 VRF: MGMT - Maximum routes warning limit mismatch - Expected: 9000, Actual: 0", ], }, },