diff --git a/anta/tests/routing/bgp.py b/anta/tests/routing/bgp.py index 6a7002356..70d2a6fcb 100644 --- a/anta/tests/routing/bgp.py +++ b/anta/tests/routing/bgp.py @@ -685,6 +685,8 @@ def test(self) -> None: class VerifyBGPPeerMPCaps(AntaTest): """Verifies the multiprotocol capabilities of a BGP peer in a specified VRF. + Supports `strict: True` to verify that only the specified capabilities are configured, requiring an exact match. + Expected Results ---------------- * Success: The test will pass if the BGP peer's multiprotocol capabilities are advertised, received, and enabled in the specified VRF. @@ -699,6 +701,7 @@ class VerifyBGPPeerMPCaps(AntaTest): bgp_peers: - peer_address: 172.30.11.1 vrf: default + strict: False capabilities: - ipv4Unicast ``` @@ -722,6 +725,8 @@ class BgpPeer(BaseModel): """IPv4 address of a BGP peer.""" vrf: str = "default" """Optional VRF for BGP peer. If not provided, it defaults to `default`.""" + strict: bool = False + """If True, requires exact matching of provided capabilities. Defaults to False.""" capabilities: list[MultiProtocolCaps] """List of multiprotocol capabilities to be verified.""" @@ -730,14 +735,14 @@ def test(self) -> None: """Main test function for VerifyBGPPeerMPCaps.""" failures: dict[str, Any] = {"bgp_peers": {}} - # Iterate over each bgp peer + # Iterate over each bgp peer. for bgp_peer in self.inputs.bgp_peers: peer = str(bgp_peer.peer_address) vrf = bgp_peer.vrf capabilities = bgp_peer.capabilities failure: dict[str, dict[str, dict[str, Any]]] = {"bgp_peers": {peer: {vrf: {}}}} - # Check if BGP output exists + # Check if BGP output exists. if ( not (bgp_output := get_value(self.instance_commands[0].json_output, f"vrfs.{vrf}.peerList")) or (bgp_output := get_item(bgp_output, "peerAddress", peer)) is None @@ -746,8 +751,17 @@ def test(self) -> None: failures = deep_update(failures, failure) continue - # Check each capability + # Fetching the capabilities output. bgp_output = get_value(bgp_output, "neighborCapabilities.multiprotocolCaps") + + if bgp_peer.strict and sorted(capabilities) != sorted(bgp_output): + failure["bgp_peers"][peer][vrf] = { + "status": f"Expected only `{', '.join(capabilities)}` capabilities should be listed but found `{', '.join(bgp_output)}` instead." + } + failures = deep_update(failures, failure) + continue + + # Check each capability for capability in capabilities: capability_output = bgp_output.get(capability) diff --git a/examples/tests.yaml b/examples/tests.yaml index 1ad8e28db..f5a5ca46b 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -546,6 +546,7 @@ anta.tests.routing: bgp_peers: - peer_address: 172.30.11.1 vrf: default + strict: False capabilities: - ipv4Unicast - VerifyBGPPeerASNCap: diff --git a/tests/units/anta_tests/routing/test_bgp.py b/tests/units/anta_tests/routing/test_bgp.py index 47db8e60b..b76939bd5 100644 --- a/tests/units/anta_tests/routing/test_bgp.py +++ b/tests/units/anta_tests/routing/test_bgp.py @@ -2200,6 +2200,152 @@ ], }, }, + { + "name": "success-strict", + "test": VerifyBGPPeerMPCaps, + "eos_data": [ + { + "vrfs": { + "default": { + "peerList": [ + { + "peerAddress": "172.30.11.1", + "neighborCapabilities": { + "multiprotocolCaps": { + "ipv4Unicast": { + "advertised": True, + "received": True, + "enabled": True, + }, + "ipv4MplsLabels": { + "advertised": True, + "received": True, + "enabled": True, + }, + } + }, + } + ] + }, + "MGMT": { + "peerList": [ + { + "peerAddress": "172.30.11.10", + "neighborCapabilities": { + "multiprotocolCaps": { + "ipv4Unicast": { + "advertised": True, + "received": True, + "enabled": True, + }, + "ipv4MplsVpn": { + "advertised": True, + "received": True, + "enabled": True, + }, + } + }, + } + ] + }, + } + } + ], + "inputs": { + "bgp_peers": [ + { + "peer_address": "172.30.11.1", + "vrf": "default", + "strict": True, + "capabilities": ["Ipv4 Unicast", "ipv4 Mpls labels"], + }, + { + "peer_address": "172.30.11.10", + "vrf": "MGMT", + "strict": True, + "capabilities": ["ipv4 Unicast", "ipv4 MplsVpn"], + }, + ] + }, + "expected": {"result": "success"}, + }, + { + "name": "failure-srict", + "test": VerifyBGPPeerMPCaps, + "eos_data": [ + { + "vrfs": { + "default": { + "peerList": [ + { + "peerAddress": "172.30.11.1", + "neighborCapabilities": { + "multiprotocolCaps": { + "ipv4Unicast": { + "advertised": True, + "received": True, + "enabled": True, + }, + "ipv4MplsLabels": { + "advertised": True, + "received": True, + "enabled": True, + }, + } + }, + } + ] + }, + "MGMT": { + "peerList": [ + { + "peerAddress": "172.30.11.10", + "neighborCapabilities": { + "multiprotocolCaps": { + "ipv4Unicast": { + "advertised": True, + "received": True, + "enabled": True, + }, + "ipv4MplsVpn": { + "advertised": False, + "received": True, + "enabled": True, + }, + } + }, + } + ] + }, + } + } + ], + "inputs": { + "bgp_peers": [ + { + "peer_address": "172.30.11.1", + "vrf": "default", + "strict": True, + "capabilities": ["Ipv4 Unicast"], + }, + { + "peer_address": "172.30.11.10", + "vrf": "MGMT", + "strict": True, + "capabilities": ["ipv4MplsVpn", "L2vpnEVPN"], + }, + ] + }, + "expected": { + "result": "failure", + "messages": [ + "Following BGP peer multiprotocol capabilities are not found or not ok:\n{'bgp_peers': {'172.30.11.1': " + "{'default': {'status': 'Expected only `ipv4Unicast` capabilities should be listed but found `ipv4Unicast, ipv4MplsLabels` instead.'}}," + " '172.30.11.10': {'MGMT': {'status': 'Expected only `ipv4MplsVpn, l2VpnEvpn` capabilities should be listed but found `ipv4Unicast, " + "ipv4MplsVpn` instead.'}}}}" + ], + }, + }, { "name": "success", "test": VerifyBGPPeerASNCap,