diff --git a/tests/topotests/bgp_bmp/bgpbmp.py b/tests/topotests/bgp_bmp/bgpbmp.py new file mode 100644 index 000000000000..31c39e4b2bf6 --- /dev/null +++ b/tests/topotests/bgp_bmp/bgpbmp.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later + +# Copyright 2023, 6wind +import json +import os + +from lib import topotest +from lib.topolog import logger + +# remember the last sequence number of the logging messages +SEQ = 0 + + +def get_bmp_messages(bmp_collector, bmp_log_file): + """ + Read the BMP logging messages. + """ + messages = [] + text_output = bmp_collector.run(f"cat {bmp_log_file}") + + for m in text_output.splitlines(): + # some output in the bash can break the message decoding + try: + messages.append(json.loads(m)) + except Exception as e: + logger.warning(str(e) + " message: {}".format(str(m))) + continue + + if not messages: + logger.error("Bad BMP log format, check your BMP server") + + return messages + + +def bmp_update_seq(bmp_collector, bmp_log_file): + global SEQ + + messages = get_bmp_messages(bmp_collector, bmp_log_file) + + if len(messages): + SEQ = messages[-1]["seq"] + + +def bmp_update_expected_files( + bmp_actual, expected_prefixes, bmp_log_type, policy, step, bmp_client +): + tgen = get_topogen() + + with open(f"/tmp/bmp-{bmp_log_type}-{policy}-step{step}.json", "w") as json_file: + json.dump(bmp_actual, json_file, indent=4) + + out = bmp_client.vtysh_cmd("show bgp vrf vrf1 ipv4 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" in pfx: + continue + filtered_out["routes"][pfx] = None + + # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done + with open(f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + out = tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv6 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" not in pfx: + continue + filtered_out["routes"][pfx] = None + + with open(f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + +def bmp_check_for_prefixes( + expected_prefixes, + bmp_log_type, + policy, + step, + bmp_collector, + bmp_log_file, + bmp_client, + expected_json_path, + update_expected_json, + loc_rib, +): + """ + Check for the presence of the given prefixes in the BMP server logs with + the given message type and the set policy. + + """ + global SEQ + + # we care only about the new messages + messages = [ + m + for m in sorted( + get_bmp_messages(bmp_collector, bmp_log_file), key=lambda d: d["seq"] + ) + if m["seq"] > SEQ + ] + + # create empty initial files + # for step in $(seq 1); do + # for i in "update" "withdraw"; do + # for j in "pre-policy" "post-policy" "loc-rib"; do + # echo '{"null": {}}'> bmp-$i-$j-step$step.json + # done + # done + # done + + ref_file = f"{expected_json_path}/bmp-{bmp_log_type}-{policy}-step{step}.json" + expected = json.loads(open(ref_file).read()) + + # Build actual json from logs + actual = {} + for m in messages: + if ( + "bmp_log_type" in m.keys() + and "ip_prefix" in m.keys() + and m["ip_prefix"] in expected_prefixes + and m["bmp_log_type"] == bmp_log_type + and m["policy"] == policy + ): + policy_dict = actual.setdefault(m["policy"], {}) + bmp_log_type_dict = policy_dict.setdefault(m["bmp_log_type"], {}) + + # Add or update the ip_prefix dictionary with filtered key-value pairs + bmp_log_type_dict[m["ip_prefix"]] = { + k: v + for k, v in sorted(m.items()) + # filter out variable keys + if k not in ["timestamp", "seq", "nxhp_link-local"] + and ( + # When policy is loc-rib, the peer-distinguisher is 0:0 + # for the default VRF or the RD if any or the 0:. + # 0: is used to distinguished. RFC7854 says: "If the + # peer is a "Local Instance Peer", it is set to a unique, + # locally defined value." The value is not tested because it + # is variable. + k != "peer_distinguisher" + or policy != loc_rib + or v == "0:0" + or not v.startswith("0:") + ) + } + + # build expected JSON files + if ( + update_expected_json + and actual + and set(actual.get(policy, {}).get(bmp_log_type, {}).keys()) + == set(expected_prefixes) + ): + update_expected_files( + actual, expected_prefixes, bmp_log_type, policy, step, bmp_client + ) + + return topotest.json_cmp(actual, expected, exact=True) + + +def bmp_check_for_peer_message( + expected_peers, bmp_log_type, bmp_collector, bmp_log_file +): + """ + Check for the presence of a peer up message for the peer + """ + global SEQ + + # we care only about the new messages + messages = [ + m + for m in sorted( + get_bmp_messages(bmp_collector, bmp_log_file), key=lambda d: d["seq"] + ) + if m["seq"] > SEQ + ] + + # get the list of pairs (prefix, policy, seq) for the given message type + peers = [ + m["peer_ip"] + for m in messages + if "peer_ip" in m.keys() and m["bmp_log_type"] == bmp_log_type + ] + + # check for prefixes + for ep in expected_peers: + if ep not in peers: + msg = "The peer {} is not present in the {} log messages." + logger.debug(msg.format(ep, bmp_log_type)) + return False + + SEQ = messages[-1]["seq"] + return True diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json b/tests/topotests/bgp_bmp/bmp1vrf/bmp-update-loc-rib-step1.json similarity index 100% rename from tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json rename to tests/topotests/bgp_bmp/bmp1vrf/bmp-update-loc-rib-step1.json diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json b/tests/topotests/bgp_bmp/bmp1vrf/bmp-update-post-policy-step1.json similarity index 100% rename from tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json rename to tests/topotests/bgp_bmp/bmp1vrf/bmp-update-post-policy-step1.json diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json b/tests/topotests/bgp_bmp/bmp1vrf/bmp-update-pre-policy-step1.json similarity index 100% rename from tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json rename to tests/topotests/bgp_bmp/bmp1vrf/bmp-update-pre-policy-step1.json diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json b/tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-loc-rib-step1.json similarity index 100% rename from tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json rename to tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-loc-rib-step1.json diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json b/tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-post-policy-step1.json similarity index 100% rename from tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json rename to tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-post-policy-step1.json diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json b/tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-pre-policy-step1.json similarity index 100% rename from tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json rename to tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-pre-policy-step1.json diff --git a/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf b/tests/topotests/bgp_bmp/r1vrf/bgpd.conf similarity index 100% rename from tests/topotests/bgp_bmp_vrf/r1/bgpd.conf rename to tests/topotests/bgp_bmp/r1vrf/bgpd.conf diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json b/tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv4-update-step1.json similarity index 91% rename from tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json rename to tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv4-update-step1.json index 038c87ca9dd0..dc0228db61f0 100644 --- a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json +++ b/tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv4-update-step1.json @@ -9,7 +9,6 @@ "nexthops": [ { "ip": "192.168.0.2", - "hostname": "r2", "afi": "ipv4", "used": true } diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json b/tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv4-withdraw-step1.json similarity index 100% rename from tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json rename to tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv4-withdraw-step1.json diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json b/tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv6-update-step1.json similarity index 88% rename from tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json rename to tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv6-update-step1.json index db34220149e2..64c8622ab523 100644 --- a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json +++ b/tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv6-update-step1.json @@ -9,12 +9,10 @@ "nexthops": [ { "ip": "192:168::2", - "hostname": "r2", "afi": "ipv6", "scope": "global" }, { - "hostname": "r2", "afi": "ipv6", "scope": "link-local", "used": true diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json b/tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv6-withdraw-step1.json similarity index 100% rename from tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json rename to tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv6-withdraw-step1.json diff --git a/tests/topotests/bgp_bmp_vrf/r1/zebra.conf b/tests/topotests/bgp_bmp/r1vrf/zebra.conf similarity index 66% rename from tests/topotests/bgp_bmp_vrf/r1/zebra.conf rename to tests/topotests/bgp_bmp/r1vrf/zebra.conf index 0b523c9e18d8..a242eadce8a7 100644 --- a/tests/topotests/bgp_bmp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_bmp/r1vrf/zebra.conf @@ -1,7 +1,7 @@ -interface r1-eth0 +interface r1vrf-eth0 ip address 192.0.2.1/24 ! -interface r1-eth1 +interface r1vrf-eth1 ip address 192.168.0.1/24 ipv6 address 192:168::1/64 ! diff --git a/tests/topotests/bgp_bmp_vrf/r2/bgpd.conf b/tests/topotests/bgp_bmp/r2vrf/bgpd.conf similarity index 100% rename from tests/topotests/bgp_bmp_vrf/r2/bgpd.conf rename to tests/topotests/bgp_bmp/r2vrf/bgpd.conf diff --git a/tests/topotests/bgp_bmp_vrf/r2/zebra.conf b/tests/topotests/bgp_bmp/r2vrf/zebra.conf similarity index 72% rename from tests/topotests/bgp_bmp_vrf/r2/zebra.conf rename to tests/topotests/bgp_bmp/r2vrf/zebra.conf index 9d82bfe2df5c..9a8da2d95fbd 100644 --- a/tests/topotests/bgp_bmp_vrf/r2/zebra.conf +++ b/tests/topotests/bgp_bmp/r2vrf/zebra.conf @@ -1,8 +1,8 @@ -interface r2-eth0 +interface r2vrf-eth0 ip address 192.168.0.2/24 ipv6 address 192:168::2/64 ! -interface r2-eth1 +interface r2vrf-eth1 ip address 172.31.0.2/24 ipv6 address 172:31::2/64 ! diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py deleted file mode 100644 index 658ad2b99a2c..000000000000 --- a/tests/topotests/bgp_bmp/test_bgp_bmp.py +++ /dev/null @@ -1,476 +0,0 @@ -#!/usr/bin/env python -# SPDX-License-Identifier: ISC - -# Copyright 2023 6WIND S.A. -# Authored by Farid Mihoub -# - -""" -test_bgp_bmp.py: Test BGP BMP functionalities - - +------+ +------+ +------+ - | | | | | | - | BMP1 |------------| R1 |---------------| R2 | - | | | | | | - +------+ +------+ +------+ - -Setup two routers R1 and R2 with one link configured with IPv4 and -IPv6 addresses. -Configure BGP in R1 and R2 to exchange prefixes from -the latter to the first router. -Setup a link between R1 and the BMP server, activate the BMP feature in R1 -and ensure the monitored BGP sessions logs are well present on the BMP server. -""" - -from functools import partial -from ipaddress import ip_network -import json -import os -import pytest -import sys - -# Save the Current Working Directory to find configuration files. -CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join("../")) -sys.path.append(os.path.join("../lib/")) - -# pylint: disable=C0413 -# Import topogen and topotest helpers -from lib import topotest -from lib.bgp import verify_bgp_convergence_from_running_config -from lib.topogen import Topogen, TopoRouter, get_topogen -from lib.topolog import logger - -pytestmark = [pytest.mark.bgpd] - -# remember the last sequence number of the logging messages -SEQ = 0 - -PRE_POLICY = "pre-policy" -POST_POLICY = "post-policy" -LOC_RIB = "loc-rib" - -UPDATE_EXPECTED_JSON = False -DEBUG_PCAP = False - - -def build_topo(tgen): - tgen.add_router("r1") - tgen.add_router("r2") - tgen.add_bmp_server("bmp1", ip="192.0.2.10", defaultRoute="via 192.0.2.1") - - switch = tgen.add_switch("s1") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["bmp1"]) - - tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth1", "r2-eth0") - - -def setup_module(mod): - tgen = Topogen(build_topo, mod.__name__) - tgen.start_topology() - - if DEBUG_PCAP: - tgen.gears["r1"].run("rm /tmp/bmp.pcap") - tgen.gears["r1"].run( - "tcpdump -nni r1-eth0 -s 0 -w /tmp/bmp.pcap &", stdout=None - ) - - for rname, router in tgen.routers().items(): - router.load_config( - TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) - ) - router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, "{}/bgpd.conf".format(rname)), - "-M bmp", - ) - - tgen.start_router() - - logger.info("starting BMP servers") - for bmp_name, server in tgen.get_bmp_servers().items(): - server.start(log_file=os.path.join(tgen.logdir, bmp_name, "bmp.log")) - - -def teardown_module(_mod): - tgen = get_topogen() - tgen.stop_topology() - - -def test_bgp_convergence(): - tgen = get_topogen() - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - result = verify_bgp_convergence_from_running_config(tgen, dut="r1") - assert result is True, "BGP is not converging" - - -def get_bmp_messages(): - """ - Read the BMP logging messages. - """ - messages = [] - tgen = get_topogen() - text_output = tgen.gears["bmp1"].run( - "cat {}".format(os.path.join(tgen.logdir, "bmp1", "bmp.log")) - ) - - for m in text_output.splitlines(): - # some output in the bash can break the message decoding - try: - messages.append(json.loads(m)) - except Exception as e: - logger.warning(str(e) + " message: {}".format(str(m))) - continue - - if not messages: - logger.error("Bad BMP log format, check your BMP server") - - return messages - - -def update_seq(): - global SEQ - - messages = get_bmp_messages() - - if len(messages): - SEQ = messages[-1]["seq"] - - -def update_expected_files(bmp_actual, expected_prefixes, bmp_log_type, policy, step): - tgen = get_topogen() - - with open(f"/tmp/bmp-{bmp_log_type}-{policy}-step{step}.json", "w") as json_file: - json.dump(bmp_actual, json_file, indent=4) - - if step == 2: # vpn - rd = "444:2" - out = tgen.gears["r1"].vtysh_cmd("show bgp ipv4 vpn json", isjson=True) - filtered_out = { - "routes": { - "routeDistinguishers": { - rd: { - prefix: route_info - for prefix, route_info in out["routes"] - .get("routeDistinguishers", {}) - .get(rd, {}) - .items() - if prefix in expected_prefixes - } - } - } - } - if bmp_log_type == "withdraw": - for pfx in expected_prefixes: - if "::" in pfx: - continue - filtered_out["routes"]["routeDistinguishers"][rd][pfx] = None - - # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done - with open( - f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w" - ) as json_file: - json.dump(filtered_out, json_file, indent=4) - - rd = "555:2" - out = tgen.gears["r1"].vtysh_cmd("show bgp ipv6 vpn json", isjson=True) - filtered_out = { - "routes": { - "routeDistinguishers": { - rd: { - prefix: route_info - for prefix, route_info in out["routes"] - .get("routeDistinguishers", {}) - .get(rd, {}) - .items() - if prefix in expected_prefixes - } - } - } - } - if bmp_log_type == "withdraw": - for pfx in expected_prefixes: - if "::" not in pfx: - continue - filtered_out["routes"]["routeDistinguishers"][rd][pfx] = None - with open( - f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w" - ) as json_file: - json.dump(filtered_out, json_file, indent=4) - - return - - out = tgen.gears["r1"].vtysh_cmd("show bgp ipv4 json", isjson=True) - filtered_out = { - "routes": { - prefix: route_info - for prefix, route_info in out["routes"].items() - if prefix in expected_prefixes - } - } - if bmp_log_type == "withdraw": - for pfx in expected_prefixes: - if "::" in pfx: - continue - filtered_out["routes"][pfx] = None - - # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done - with open(f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w") as json_file: - json.dump(filtered_out, json_file, indent=4) - - out = tgen.gears["r1"].vtysh_cmd("show bgp ipv6 json", isjson=True) - filtered_out = { - "routes": { - prefix: route_info - for prefix, route_info in out["routes"].items() - if prefix in expected_prefixes - } - } - if bmp_log_type == "withdraw": - for pfx in expected_prefixes: - if "::" not in pfx: - continue - filtered_out["routes"][pfx] = None - with open(f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w") as json_file: - json.dump(filtered_out, json_file, indent=4) - - -def check_for_prefixes(expected_prefixes, bmp_log_type, policy, step): - """ - Check for the presence of the given prefixes in the BMP server logs with - the given message type and the set policy. - - """ - global SEQ - - # we care only about the new messages - messages = [ - m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ - ] - - # create empty initial files - # for step in $(seq 2); do - # for i in "update" "withdraw"; do - # for j in "pre-policy" "post-policy" "loc-rib"; do - # echo '{"null": {}}'> bmp-$i-$j-step$step.json - # done - # done - # done - - ref_file = f"{CWD}/bmp1/bmp-{bmp_log_type}-{policy}-step{step}.json" - expected = json.loads(open(ref_file).read()) - - # Build actual json from logs - actual = {} - for m in messages: - if ( - "bmp_log_type" in m.keys() - and "ip_prefix" in m.keys() - and m["ip_prefix"] in expected_prefixes - and m["bmp_log_type"] == bmp_log_type - and m["policy"] == policy - ): - policy_dict = actual.setdefault(m["policy"], {}) - bmp_log_type_dict = policy_dict.setdefault(m["bmp_log_type"], {}) - - # Add or update the ip_prefix dictionary with filtered key-value pairs - bmp_log_type_dict[m["ip_prefix"]] = { - k: v - for k, v in sorted(m.items()) - # filter out variable keys - if k not in ["timestamp", "seq", "nxhp_link-local"] - and ( - # When policy is loc-rib, the peer-distinguisher is 0:0 - # for the default VRF or the RD if any or the 0:. - # 0: is used to distinguished. RFC7854 says: "If the - # peer is a "Local Instance Peer", it is set to a unique, - # locally defined value." The value is not tested because it - # is variable. - k != "peer_distinguisher" - or policy != LOC_RIB - or v == "0:0" - or not v.startswith("0:") - ) - } - - # build expected JSON files - if ( - UPDATE_EXPECTED_JSON - and actual - and set(actual.get(policy, {}).get(bmp_log_type, {}).keys()) - == set(expected_prefixes) - ): - update_expected_files(actual, expected_prefixes, bmp_log_type, policy, step) - - return topotest.json_cmp(actual, expected, exact=True) - - -def check_for_peer_message(expected_peers, bmp_log_type): - """ - Check for the presence of a peer up message for the peer - """ - global SEQ - # we care only about the new messages - messages = [ - m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ - ] - - # get the list of pairs (prefix, policy, seq) for the given message type - peers = [ - m["peer_ip"] - for m in messages - if "peer_ip" in m.keys() and m["bmp_log_type"] == bmp_log_type - ] - - # check for prefixes - for ep in expected_peers: - if ep not in peers: - msg = "The peer {} is not present in the {} log messages." - logger.debug(msg.format(ep, bmp_log_type)) - return False - - SEQ = messages[-1]["seq"] - return True - - -def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): - """ - Configure the bgp prefixes. - """ - withdraw = "no " if not update else "" - vrf = " vrf {}".format(vrf) if vrf else "" - for p in prefixes: - ip = ip_network(p) - cmd = [ - "conf t\n", - "router bgp {}{}\n".format(asn, vrf), - "address-family ipv{} {}\n".format(ip.version, safi), - "{}network {}\n".format(withdraw, ip), - "exit-address-family\n", - ] - logger.debug("setting prefix: ipv{} {} {}".format(ip.version, safi, ip)) - tgen.gears[node].vtysh_cmd("".join(cmd)) - - -def _test_prefixes(policy, vrf=None, step=0): - """ - Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. - Check if the previous actions are logged in the BMP server with the right - message type and the right policy. - """ - tgen = get_topogen() - - safi = "vpn" if vrf else "unicast" - - prefixes = ["172.31.0.15/32", "2001::1111/128"] - - for type in ("update", "withdraw"): - update_seq() - - configure_prefixes( - tgen, "r2", 65502, "unicast", prefixes, vrf=vrf, update=(type == "update") - ) - - logger.info(f"checking for prefixes {type}") - - for ipver in [4, 6]: - if UPDATE_EXPECTED_JSON: - continue - ref_file = "{}/r1/show-bgp-ipv{}-{}-step{}.json".format( - CWD, ipver, type, step - ) - expected = json.loads(open(ref_file).read()) - - test_func = partial( - topotest.router_json_cmp, - tgen.gears["r1"], - f"show bgp ipv{ipver} {safi} json", - expected, - ) - _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) - assertmsg = f"r1: BGP IPv{ipver} convergence failed" - assert res is None, assertmsg - - # check - test_func = partial(check_for_prefixes, prefixes, type, policy, step) - success, res = topotest.run_and_expect(test_func, None, count=30, wait=1) - assert success, "Checking the updated prefixes has failed ! %s" % res - - -def test_bmp_server_logging(): - """ - Assert the logging of the bmp server. - """ - - def check_for_log_file(): - tgen = get_topogen() - output = tgen.gears["bmp1"].run( - "ls {}".format(os.path.join(tgen.logdir, "bmp1")) - ) - if "bmp.log" not in output: - return False - return True - - success, _ = topotest.run_and_expect(check_for_log_file, True, count=30, wait=1) - assert success, "The BMP server is not logging" - - -def test_peer_up(): - """ - Checking for BMP peers up messages - """ - - peers = ["192.168.0.2", "192:168::2"] - - logger.info("checking for BMP peers up messages") - - test_func = partial(check_for_peer_message, peers, "peer up") - success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) - assert success, "Checking the updated prefixes has been failed !." - - -def test_bmp_bgp_unicast(): - """ - Add/withdraw bgp unicast prefixes and check the bmp logs. - """ - logger.info("*** Unicast prefixes pre-policy logging ***") - _test_prefixes(PRE_POLICY, step=1) - logger.info("*** Unicast prefixes post-policy logging ***") - _test_prefixes(POST_POLICY, step=1) - logger.info("*** Unicast prefixes loc-rib logging ***") - _test_prefixes(LOC_RIB, step=1) - - -def test_bmp_bgp_vpn(): - # check for the prefixes in the BMP server logging file - logger.info("***** VPN prefixes pre-policy logging *****") - _test_prefixes(PRE_POLICY, vrf="vrf1", step=2) - logger.info("***** VPN prefixes post-policy logging *****") - _test_prefixes(POST_POLICY, vrf="vrf1", step=2) - logger.info("***** VPN prefixes loc-rib logging *****") - _test_prefixes(LOC_RIB, vrf="vrf1", step=2) - - -def test_peer_down(): - """ - Checking for BMP peers down messages - """ - tgen = get_topogen() - - tgen.gears["r2"].vtysh_cmd("clear bgp *") - - peers = ["192.168.0.2", "192:168::2"] - - logger.info("checking for BMP peers down messages") - - test_func = partial(check_for_peer_message, peers, "peer down") - success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) - assert success, "Checking the updated prefixes has been failed !." - - -if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp_1.py b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py new file mode 100644 index 000000000000..cc7bc31041d9 --- /dev/null +++ b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2023 6WIND S.A. +# Authored by Farid Mihoub +# + +""" +test_bgp_bmp.py: Test BGP BMP functionalities + + +------+ +------+ +------+ + | | | | | | + | BMP1 |------------| R1 |---------------| R2 | + | | | | | | + +------+ +------+ +------+ + +Setup two routers R1 and R2 with one link configured with IPv4 and +IPv6 addresses. +Configure BGP in R1 and R2 to exchange prefixes from +the latter to the first router. +Setup a link between R1 and the BMP server, activate the BMP feature in R1 +and ensure the monitored BGP sessions logs are well present on the BMP server. +""" + +from functools import partial +import json +import os +import pytest +import sys + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.bgp import verify_bgp_convergence_from_running_config +from lib.bgp import bgp_configure_prefixes +from .bgpbmp import ( + bmp_check_for_prefixes, + bmp_check_for_peer_message, + bmp_update_seq, +) +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + +PRE_POLICY = "pre-policy" +POST_POLICY = "post-policy" +LOC_RIB = "loc-rib" + +UPDATE_EXPECTED_JSON = False +DEBUG_PCAP = False + + +def build_topo(tgen): + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_bmp_server("bmp1", ip="192.0.2.10", defaultRoute="via 192.0.2.1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["bmp1"]) + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth1", "r2-eth0") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + if DEBUG_PCAP: + tgen.gears["r1"].run("rm /tmp/bmp.pcap") + tgen.gears["r1"].run( + "tcpdump -nni r1-eth0 -s 0 -w /tmp/bmp.pcap &", stdout=None + ) + + for rname, router in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M bmp", + ) + + tgen.start_router() + + logger.info("starting BMP servers") + for bmp_name, server in tgen.get_bmp_servers().items(): + server.start(log_file=os.path.join(tgen.logdir, bmp_name, "bmp.log")) + + +def teardown_module(_mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + result = verify_bgp_convergence_from_running_config(tgen, dut="r1") + assert result is True, "BGP is not converging" + + +def _test_prefixes(policy, vrf=None, step=0): + """ + Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. + Check if the previous actions are logged in the BMP server with the right + message type and the right policy. + """ + tgen = get_topogen() + + safi = "vpn" if vrf else "unicast" + + prefixes = ["172.31.0.15/32", "2001::1111/128"] + + for type in ("update", "withdraw"): + bmp_update_seq(tgen.gears["bmp1"], os.path.join(tgen.logdir, "bmp1", "bmp.log")) + + bgp_configure_prefixes( + tgen.gears["r2"], + 65502, + "unicast", + prefixes, + vrf=vrf, + update=(type == "update"), + ) + + logger.info(f"checking for prefixes {type}") + + for ipver in [4, 6]: + if UPDATE_EXPECTED_JSON: + continue + ref_file = "{}/r1/show-bgp-ipv{}-{}-step{}.json".format( + CWD, ipver, type, step + ) + expected = json.loads(open(ref_file).read()) + + test_func = partial( + topotest.router_json_cmp, + tgen.gears["r1"], + f"show bgp ipv{ipver} {safi} json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = f"r1: BGP IPv{ipver} convergence failed" + assert res is None, assertmsg + + # check + test_func = partial( + bmp_check_for_prefixes, + prefixes, + type, + policy, + step, + tgen.gears["bmp1"], + os.path.join(tgen.logdir, "bmp1", "bmp.log"), + tgen.gears["r1"], + f"{CWD}/bmp1", + UPDATE_EXPECTED_JSON, + LOC_RIB, + ) + success, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert success, "Checking the updated prefixes has failed ! %s" % res + + +def test_bmp_server_logging(): + """ + Assert the logging of the bmp server. + """ + + def check_for_log_file(): + tgen = get_topogen() + output = tgen.gears["bmp1"].run( + "ls {}".format(os.path.join(tgen.logdir, "bmp1")) + ) + if "bmp.log" not in output: + return False + return True + + success, _ = topotest.run_and_expect(check_for_log_file, True, count=30, wait=1) + assert success, "The BMP server is not logging" + + +def test_peer_up(): + """ + Checking for BMP peers up messages + """ + + tgen = get_topogen() + peers = ["192.168.0.2", "192:168::2"] + + logger.info("checking for BMP peers up messages") + + test_func = partial( + bmp_check_for_peer_message, + peers, + "peer up", + tgen.gears["bmp1"], + os.path.join(tgen.logdir, "bmp1", "bmp.log"), + ) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) + assert success, "Checking the updated prefixes has been failed !." + + +def test_bmp_bgp_unicast(): + """ + Add/withdraw bgp unicast prefixes and check the bmp logs. + """ + logger.info("*** Unicast prefixes pre-policy logging ***") + _test_prefixes(PRE_POLICY, step=1) + logger.info("*** Unicast prefixes post-policy logging ***") + _test_prefixes(POST_POLICY, step=1) + logger.info("*** Unicast prefixes loc-rib logging ***") + _test_prefixes(LOC_RIB, step=1) + + +def test_bmp_bgp_vpn(): + # check for the prefixes in the BMP server logging file + logger.info("***** VPN prefixes pre-policy logging *****") + _test_prefixes(PRE_POLICY, vrf="vrf1", step=2) + logger.info("***** VPN prefixes post-policy logging *****") + _test_prefixes(POST_POLICY, vrf="vrf1", step=2) + logger.info("***** VPN prefixes loc-rib logging *****") + _test_prefixes(LOC_RIB, vrf="vrf1", step=2) + + +def test_peer_down(): + """ + Checking for BMP peers down messages + """ + tgen = get_topogen() + + tgen.gears["r2"].vtysh_cmd("clear bgp *") + + peers = ["192.168.0.2", "192:168::2"] + + logger.info("checking for BMP peers down messages") + + test_func = partial( + bmp_check_for_peer_message, + peers, + "peer down", + tgen.gears["bmp1"], + os.path.join(tgen.logdir, "bmp1", "bmp.log"), + ) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) + assert success, "Checking the updated prefixes has been failed !." + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp_2.py b/tests/topotests/bgp_bmp/test_bgp_bmp_2.py new file mode 100644 index 000000000000..3c02d67ba520 --- /dev/null +++ b/tests/topotests/bgp_bmp/test_bgp_bmp_2.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2023 6WIND S.A. +# Authored by Farid Mihoub +# + +""" +test_bgp_bmp.py: Test BGP BMP functionalities + + +------+ +------+ +------+ + | | | | | | + | BMP1 |------------| R1 |---------------| R2 | + | | | | | | + +------+ +------+ +------+ + +Setup two routers R1 and R2 with one link configured with IPv4 and +IPv6 addresses. +Configure BGP in R1 and R2 to exchange prefixes from +the latter to the first router. +Setup a link between R1 and the BMP server, activate the BMP feature in R1 +and ensure the monitored BGP sessions logs are well present on the BMP server. +""" + +from functools import partial +import json +import os +import platform +import pytest +import sys + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.bgp import verify_bgp_convergence_from_running_config +from lib.bgp import bgp_configure_prefixes +from .bgpbmp import ( + bmp_check_for_prefixes, + bmp_check_for_peer_message, + bmp_update_seq, +) + + +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + +PRE_POLICY = "pre-policy" +POST_POLICY = "post-policy" +LOC_RIB = "loc-rib" + +UPDATE_EXPECTED_JSON = False +DEBUG_PCAP = False + + +def build_topo(tgen): + tgen.add_router("r1vrf") + tgen.add_router("r2vrf") + tgen.add_bmp_server("bmp1vrf", ip="192.0.2.10", defaultRoute="via 192.0.2.1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1vrf"]) + switch.add_link(tgen.gears["bmp1vrf"]) + + tgen.add_link(tgen.gears["r1vrf"], tgen.gears["r2vrf"], "r1vrf-eth1", "r2vrf-eth0") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + tgen.net["r1vrf"].cmd( + """ +ip link add vrf1 type vrf table 10 +ip link set vrf1 up +ip link set r1vrf-eth1 master vrf1 +""" + ) + + if DEBUG_PCAP: + tgen.gears["r1vrf"].run("rm /tmp/bmp_vrf.pcap") + tgen.gears["r1vrf"].run( + "tcpdump -nni r1vrf-eth0 -s 0 -w /tmp/bmp_vrf.pcap &", stdout=None + ) + + for rname, router in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M bmp", + ) + + tgen.start_router() + + logger.info("starting BMP servers") + for bmp_name, server in tgen.get_bmp_servers().items(): + server.start(log_file=os.path.join(tgen.logdir, bmp_name, "bmp.log")) + + +def teardown_module(_mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + result = verify_bgp_convergence_from_running_config(tgen, dut="r1vrf") + assert result is True, "BGP is not converging" + + +def _test_prefixes(policy, step=1): + """ + Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. + Check if the previous actions are logged in the BMP server with the right + message type and the right policy. + """ + tgen = get_topogen() + + prefixes = ["172.31.0.15/32", "2111::1111/128"] + + for type in ("update", "withdraw"): + bmp_update_seq( + tgen.gears["bmp1vrf"], os.path.join(tgen.logdir, "bmp1vrf", "bmp.log") + ) + + # add prefixes + bgp_configure_prefixes( + tgen.gears["r2vrf"], 65502, "unicast", prefixes, update=(type == "update") + ) + + logger.info(f"checking for prefixes {type}") + + for ipver in [4, 6]: + if UPDATE_EXPECTED_JSON: + continue + ref_file = "{}/r1vrf/show-bgp-ipv{}-{}-step{}.json".format( + CWD, ipver, type, step + ) + expected = json.loads(open(ref_file).read()) + + test_func = partial( + topotest.router_json_cmp, + tgen.gears["r1vrf"], + f"show bgp vrf vrf1 ipv{ipver} json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = f"r1vrf: BGP IPv{ipver} convergence failed" + assert res is None, assertmsg + + # check + test_func = partial( + bmp_check_for_prefixes, + prefixes, + type, + policy, + step, + tgen.gears["bmp1vrf"], + os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"), + tgen.gears["r1vrf"], + f"{CWD}/bmp1vrf", + UPDATE_EXPECTED_JSON, + LOC_RIB, + ) + success, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert success, "Checking the updated prefixes has failed ! %s" % res + + +def test_bmp_server_logging(): + """ + Assert the logging of the bmp server. + """ + + def check_for_log_file(): + tgen = get_topogen() + output = tgen.gears["bmp1vrf"].run( + "ls {}".format(os.path.join(tgen.logdir, "bmp1vrf")) + ) + if "bmp.log" not in output: + return False + return True + + success, _ = topotest.run_and_expect(check_for_log_file, True, count=30, wait=1) + assert success, "The BMP server is not logging" + + +def test_peer_up(): + """ + Checking for BMP peers up messages + """ + + tgen = get_topogen() + peers = ["192.168.0.2", "192:168::2"] + + logger.info("checking for BMP peers up messages") + + test_func = partial( + bmp_check_for_peer_message, + peers, + "peer up", + tgen.gears["bmp1vrf"], + os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"), + ) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) + assert success, "Checking the updated prefixes has been failed !." + + +def test_bmp_bgp_unicast(): + """ + Add/withdraw bgp unicast prefixes and check the bmp logs. + """ + logger.info("*** Unicast prefixes pre-policy logging ***") + _test_prefixes(PRE_POLICY) + logger.info("*** Unicast prefixes post-policy logging ***") + _test_prefixes(POST_POLICY) + logger.info("*** Unicast prefixes loc-rib logging ***") + _test_prefixes(LOC_RIB) + + +def test_peer_down(): + """ + Checking for BMP peers down messages + """ + tgen = get_topogen() + + tgen.gears["r2vrf"].vtysh_cmd("clear bgp *") + + peers = ["192.168.0.2", "192:168::2"] + + logger.info("checking for BMP peers down messages") + + test_func = partial( + bmp_check_for_peer_message, + peers, + "peer down", + tgen.gears["bmp1vrf"], + os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"), + ) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) + assert success, "Checking the updated prefixes has been failed !." + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_bmp_vrf/__init__.py b/tests/topotests/bgp_bmp_vrf/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py b/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py deleted file mode 100644 index d31328bdb659..000000000000 --- a/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py +++ /dev/null @@ -1,418 +0,0 @@ -#!/usr/bin/env python -# SPDX-License-Identifier: ISC - -# Copyright 2023 6WIND S.A. -# Authored by Farid Mihoub -# - -""" -test_bgp_bmp.py: Test BGP BMP functionalities - - +------+ +------+ +------+ - | | | | | | - | BMP1 |------------| R1 |---------------| R2 | - | | | | | | - +------+ +------+ +------+ - -Setup two routers R1 and R2 with one link configured with IPv4 and -IPv6 addresses. -Configure BGP in R1 and R2 to exchange prefixes from -the latter to the first router. -Setup a link between R1 and the BMP server, activate the BMP feature in R1 -and ensure the monitored BGP sessions logs are well present on the BMP server. -""" - -from functools import partial -from ipaddress import ip_network -import json -import os -import platform -import pytest -import sys - -# Save the Current Working Directory to find configuration files. -CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join("../")) -sys.path.append(os.path.join("../lib/")) - -# pylint: disable=C0413 -# Import topogen and topotest helpers -from lib import topotest -from lib.bgp import verify_bgp_convergence_from_running_config -from lib.topogen import Topogen, TopoRouter, get_topogen -from lib.topolog import logger - -pytestmark = [pytest.mark.bgpd] - -# remember the last sequence number of the logging messages -SEQ = 0 - -PRE_POLICY = "pre-policy" -POST_POLICY = "post-policy" -LOC_RIB = "loc-rib" - -UPDATE_EXPECTED_JSON = False -DEBUG_PCAP = False - - -def build_topo(tgen): - tgen.add_router("r1") - tgen.add_router("r2") - tgen.add_bmp_server("bmp1", ip="192.0.2.10", defaultRoute="via 192.0.2.1") - - switch = tgen.add_switch("s1") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["bmp1"]) - - tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth1", "r2-eth0") - - -def setup_module(mod): - tgen = Topogen(build_topo, mod.__name__) - tgen.start_topology() - - tgen.net["r1"].cmd( - """ -ip link add vrf1 type vrf table 10 -ip link set vrf1 up -ip link set r1-eth1 master vrf1 -""" - ) - - if DEBUG_PCAP: - tgen.gears["r1"].run("rm /tmp/bmp_vrf.pcap") - tgen.gears["r1"].run( - "tcpdump -nni r1-eth0 -s 0 -w /tmp/bmp_vrf.pcap &", stdout=None - ) - - for rname, router in tgen.routers().items(): - router.load_config( - TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) - ) - router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, "{}/bgpd.conf".format(rname)), - "-M bmp", - ) - - tgen.start_router() - - logger.info("starting BMP servers") - for bmp_name, server in tgen.get_bmp_servers().items(): - server.start(log_file=os.path.join(tgen.logdir, bmp_name, "bmp.log")) - - -def teardown_module(_mod): - tgen = get_topogen() - tgen.stop_topology() - - -def test_bgp_convergence(): - tgen = get_topogen() - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - result = verify_bgp_convergence_from_running_config(tgen, dut="r1") - assert result is True, "BGP is not converging" - - -def get_bmp_messages(): - """ - Read the BMP logging messages. - """ - messages = [] - tgen = get_topogen() - text_output = tgen.gears["bmp1"].run( - "cat {}".format(os.path.join(tgen.logdir, "bmp1", "bmp.log")) - ) - - for m in text_output.splitlines(): - # some output in the bash can break the message decoding - try: - messages.append(json.loads(m)) - except Exception as e: - logger.warning(str(e) + " message: {}".format(str(m))) - continue - - if not messages: - logger.error("Bad BMP log format, check your BMP server") - - return messages - - -def update_seq(): - global SEQ - - messages = get_bmp_messages() - - if len(messages): - SEQ = messages[-1]["seq"] - - -def update_expected_files(bmp_actual, expected_prefixes, bmp_log_type, policy, step): - tgen = get_topogen() - - with open(f"/tmp/bmp-{bmp_log_type}-{policy}-step{step}.json", "w") as json_file: - json.dump(bmp_actual, json_file, indent=4) - - out = tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 json", isjson=True) - filtered_out = { - "routes": { - prefix: route_info - for prefix, route_info in out["routes"].items() - if prefix in expected_prefixes - } - } - if bmp_log_type == "withdraw": - for pfx in expected_prefixes: - if "::" in pfx: - continue - filtered_out["routes"][pfx] = None - - # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done - with open(f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w") as json_file: - json.dump(filtered_out, json_file, indent=4) - - out = tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv6 json", isjson=True) - filtered_out = { - "routes": { - prefix: route_info - for prefix, route_info in out["routes"].items() - if prefix in expected_prefixes - } - } - if bmp_log_type == "withdraw": - for pfx in expected_prefixes: - if "::" not in pfx: - continue - filtered_out["routes"][pfx] = None - - with open(f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w") as json_file: - json.dump(filtered_out, json_file, indent=4) - - -def check_for_prefixes(expected_prefixes, bmp_log_type, policy, step): - """ - Check for the presence of the given prefixes in the BMP server logs with - the given message type and the set policy. - - """ - global SEQ - - # we care only about the new messages - messages = [ - m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ - ] - - # create empty initial files - # for step in $(seq 1); do - # for i in "update" "withdraw"; do - # for j in "pre-policy" "post-policy" "loc-rib"; do - # echo '{"null": {}}'> bmp-$i-$j-step$step.json - # done - # done - # done - - ref_file = f"{CWD}/bmp1/bmp-{bmp_log_type}-{policy}-step{step}.json" - expected = json.loads(open(ref_file).read()) - - # Build actual json from logs - actual = {} - for m in messages: - if ( - "bmp_log_type" in m.keys() - and "ip_prefix" in m.keys() - and m["ip_prefix"] in expected_prefixes - and m["bmp_log_type"] == bmp_log_type - and m["policy"] == policy - ): - policy_dict = actual.setdefault(m["policy"], {}) - bmp_log_type_dict = policy_dict.setdefault(m["bmp_log_type"], {}) - - # Add or update the ip_prefix dictionary with filtered key-value pairs - bmp_log_type_dict[m["ip_prefix"]] = { - k: v - for k, v in sorted(m.items()) - # filter out variable keys - if k not in ["timestamp", "seq", "nxhp_link-local"] - and ( - # When policy is loc-rib, the peer-distinguisher is 0:0 - # for the default VRF or the RD if any or the 0:. - # 0: is used to distinguished. RFC7854 says: "If the - # peer is a "Local Instance Peer", it is set to a unique, - # locally defined value." The value is not tested because it - # is variable. - k != "peer_distinguisher" - or policy != LOC_RIB - or v == "0:0" - or not v.startswith("0:") - ) - } - - # build expected JSON files - if ( - UPDATE_EXPECTED_JSON - and actual - and set(actual.get(policy, {}).get(bmp_log_type, {}).keys()) - == set(expected_prefixes) - ): - update_expected_files(actual, expected_prefixes, bmp_log_type, policy, step) - - return topotest.json_cmp(actual, expected, exact=True) - - -def check_for_peer_message(expected_peers, bmp_log_type): - """ - Check for the presence of a peer up message for the peer - """ - global SEQ - # we care only about the new messages - messages = [ - m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ - ] - - # get the list of pairs (prefix, policy, seq) for the given message type - peers = [ - m["peer_ip"] - for m in messages - if "peer_ip" in m.keys() and m["bmp_log_type"] == bmp_log_type - ] - - # check for prefixes - for ep in expected_peers: - if ep not in peers: - msg = "The peer {} is not present in the {} log messages." - logger.debug(msg.format(ep, bmp_log_type)) - return False - - SEQ = messages[-1]["seq"] - return True - - -def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): - """ - Configure the bgp prefixes. - """ - withdraw = "no " if not update else "" - vrf = " vrf {}".format(vrf) if vrf else "" - for p in prefixes: - ip = ip_network(p) - cmd = [ - "conf t\n", - "router bgp {}{}\n".format(asn, vrf), - "address-family ipv{} {}\n".format(ip.version, safi), - "{}network {}\n".format(withdraw, ip), - "exit-address-family\n", - ] - logger.debug("setting prefix: ipv{} {} {}".format(ip.version, safi, ip)) - tgen.gears[node].vtysh_cmd("".join(cmd)) - - -def _test_prefixes(policy, step=1): - """ - Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. - Check if the previous actions are logged in the BMP server with the right - message type and the right policy. - """ - tgen = get_topogen() - - prefixes = ["172.31.0.15/32", "2111::1111/128"] - - for type in ("update", "withdraw"): - update_seq() - - # add prefixes - configure_prefixes( - tgen, "r2", 65502, "unicast", prefixes, update=(type == "update") - ) - - logger.info(f"checking for prefixes {type}") - - for ipver in [4, 6]: - if UPDATE_EXPECTED_JSON: - continue - ref_file = "{}/r1/show-bgp-ipv{}-{}-step{}.json".format( - CWD, ipver, type, step - ) - expected = json.loads(open(ref_file).read()) - - test_func = partial( - topotest.router_json_cmp, - tgen.gears["r1"], - f"show bgp vrf vrf1 ipv{ipver} json", - expected, - ) - _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) - assertmsg = f"r1: BGP IPv{ipver} convergence failed" - assert res is None, assertmsg - - # check - test_func = partial(check_for_prefixes, prefixes, type, policy, step) - success, res = topotest.run_and_expect(test_func, None, count=30, wait=1) - assert success, "Checking the updated prefixes has been failed ! %s" % res - - -def test_bmp_server_logging(): - """ - Assert the logging of the bmp server. - """ - - def check_for_log_file(): - tgen = get_topogen() - output = tgen.gears["bmp1"].run( - "ls {}".format(os.path.join(tgen.logdir, "bmp1")) - ) - if "bmp.log" not in output: - return False - return True - - success, _ = topotest.run_and_expect(check_for_log_file, True, count=30, wait=1) - assert success, "The BMP server is not logging" - - -def test_peer_up(): - """ - Checking for BMP peers up messages - """ - - peers = ["192.168.0.2", "192:168::2"] - - logger.info("checking for BMP peers up messages") - - test_func = partial(check_for_peer_message, peers, "peer up") - success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) - assert success, "Checking the updated prefixes has been failed !." - - -def test_bmp_bgp_unicast(): - """ - Add/withdraw bgp unicast prefixes and check the bmp logs. - """ - logger.info("*** Unicast prefixes pre-policy logging ***") - _test_prefixes(PRE_POLICY) - logger.info("*** Unicast prefixes post-policy logging ***") - _test_prefixes(POST_POLICY) - logger.info("*** Unicast prefixes loc-rib logging ***") - _test_prefixes(LOC_RIB) - - -def test_peer_down(): - """ - Checking for BMP peers down messages - """ - tgen = get_topogen() - - tgen.gears["r2"].vtysh_cmd("clear bgp *") - - peers = ["192.168.0.2", "192:168::2"] - - logger.info("checking for BMP peers down messages") - - test_func = partial(check_for_peer_message, peers, "peer down") - success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) - assert success, "Checking the updated prefixes has been failed !." - - -if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index bcd1c748120f..998cd556ecf0 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -5638,3 +5638,22 @@ def configure_bgp_soft_configuration(tgen, dut, neighbor_dict, direction): ) ) return True + + +def bgp_configure_prefixes(router, asn, safi, prefixes, vrf=None, update=True): + """ + Configure the bgp prefixes. + """ + withdraw = "no " if not update else "" + vrf = " vrf {}".format(vrf) if vrf else "" + for p in prefixes: + ip = ipaddress.ip_network(p) + cmd = [ + "conf t\n", + "router bgp {}{}\n".format(asn, vrf), + "address-family ipv{} {}\n".format(ip.version, safi), + "{}network {}\n".format(withdraw, ip), + "exit-address-family\n", + ] + logger.debug("setting prefix: ipv{} {} {}".format(ip.version, safi, ip)) + router.vtysh_cmd("".join(cmd))