summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/topotests/bgp_bmp/bgpbmp.py233
-rw-r--r--tests/topotests/bgp_bmp/bmp1vrf/bmp-update-loc-rib-step1.json (renamed from tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json)0
-rw-r--r--tests/topotests/bgp_bmp/bmp1vrf/bmp-update-post-policy-step1.json (renamed from tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json)0
-rw-r--r--tests/topotests/bgp_bmp/bmp1vrf/bmp-update-pre-policy-step1.json (renamed from tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json)0
-rw-r--r--tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-loc-rib-step1.json (renamed from tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json)0
-rw-r--r--tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-post-policy-step1.json (renamed from tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json)0
-rw-r--r--tests/topotests/bgp_bmp/bmp1vrf/bmp-withdraw-pre-policy-step1.json (renamed from tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json)0
-rw-r--r--tests/topotests/bgp_bmp/r1/frr.conf (renamed from tests/topotests/bgp_bmp/r1/bgpd.conf)9
-rw-r--r--tests/topotests/bgp_bmp/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_bmp/r1vrf/frr.conf (renamed from tests/topotests/bgp_bmp_vrf/r1/bgpd.conf)8
-rw-r--r--tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv4-update-step1.json (renamed from tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json)1
-rw-r--r--tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv4-withdraw-step1.json (renamed from tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json)0
-rw-r--r--tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv6-update-step1.json (renamed from tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json)2
-rw-r--r--tests/topotests/bgp_bmp/r1vrf/show-bgp-ipv6-withdraw-step1.json (renamed from tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json)0
-rw-r--r--tests/topotests/bgp_bmp/r2/frr.conf (renamed from tests/topotests/bgp_bmp/r2/bgpd.conf)8
-rw-r--r--tests/topotests/bgp_bmp/r2/zebra.conf8
-rw-r--r--tests/topotests/bgp_bmp/r2vrf/frr.conf (renamed from tests/topotests/bgp_bmp_vrf/r2/bgpd.conf)8
-rw-r--r--tests/topotests/bgp_bmp/test_bgp_bmp.py476
-rw-r--r--tests/topotests/bgp_bmp/test_bgp_bmp_1.py257
-rw-r--r--tests/topotests/bgp_bmp/test_bgp_bmp_2.py255
-rw-r--r--tests/topotests/bgp_bmp_vrf/__init__.py0
-rw-r--r--tests/topotests/bgp_bmp_vrf/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_bmp_vrf/r2/zebra.conf8
-rw-r--r--tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py418
-rw-r--r--tests/topotests/lib/bgp.py19
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py6
-rwxr-xr-xtests/topotests/lib/bmp_collector/bmpserver.py (renamed from tests/topotests/lib/bmp_collector/bmpserver)84
-rw-r--r--tests/topotests/lib/topogen.py7
28 files changed, 885 insertions, 936 deletions
diff --git a/tests/topotests/bgp_bmp/bgpbmp.py b/tests/topotests/bgp_bmp/bgpbmp.py
new file mode 100644
index 000000000..41995e2b5
--- /dev/null
+++ b/tests/topotests/bgp_bmp/bgpbmp.py
@@ -0,0 +1,233 @@
+#!/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.topogen import get_topogen
+from lib.topolog import logger
+
+# remember the last sequence number of the logging messages
+SEQ = 0
+
+
+def bmp_reset_seq():
+ global SEQ
+ 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,
+ bmp_log_folder,
+):
+ tgen = get_topogen()
+
+ with open(
+ f"{bmp_log_folder}/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 {bmp_log_folder}/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"{bmp_log_folder}/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"{bmp_log_folder}/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_folder,
+ 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
+
+ bmp_log_file = f"{bmp_log_folder}/bmp.log"
+ # 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:<vrf_id>.
+ # 0:<vrf_id> 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)
+ ):
+ bmp_update_expected_files(
+ actual,
+ expected_prefixes,
+ bmp_log_type,
+ policy,
+ step,
+ bmp_client,
+ bmp_log_folder,
+ )
+
+ 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
index ba31bf1d5..ba31bf1d5 100644
--- 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
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
index d5d9d6518..d5d9d6518 100644
--- 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
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
index e11badc04..e11badc04 100644
--- 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
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
index 37ddc09ff..37ddc09ff 100644
--- 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
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
index de84307a4..de84307a4 100644
--- 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
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
index 1c34498b7..1c34498b7 100644
--- 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
diff --git a/tests/topotests/bgp_bmp/r1/bgpd.conf b/tests/topotests/bgp_bmp/r1/frr.conf
index 485c21709..f7cb669b3 100644
--- a/tests/topotests/bgp_bmp/r1/bgpd.conf
+++ b/tests/topotests/bgp_bmp/r1/frr.conf
@@ -1,3 +1,10 @@
+interface r1-eth0
+ ip address 192.0.2.1/24
+!
+interface r1-eth1
+ ip address 192.168.0.1/24
+ ipv6 address 192:168::1/64
+!
router bgp 65501
bgp router-id 192.168.0.1
bgp log-neighbor-changes
@@ -41,7 +48,7 @@ router bgp 65501
exit-address-family
!
router bgp 65501 vrf vrf1
- bgp router_id 192.168.0.1
+ bgp router-id 192.168.0.1
bgp log-neighbor-changes
address-family ipv4 unicast
label vpn export 101
diff --git a/tests/topotests/bgp_bmp/r1/zebra.conf b/tests/topotests/bgp_bmp/r1/zebra.conf
deleted file mode 100644
index 0b523c9e1..000000000
--- a/tests/topotests/bgp_bmp/r1/zebra.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-interface r1-eth0
- ip address 192.0.2.1/24
-!
-interface r1-eth1
- ip address 192.168.0.1/24
- ipv6 address 192:168::1/64
-!
diff --git a/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf b/tests/topotests/bgp_bmp/r1vrf/frr.conf
index 961e20498..cb8a7d2b1 100644
--- a/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf
+++ b/tests/topotests/bgp_bmp/r1vrf/frr.conf
@@ -1,3 +1,10 @@
+interface r1vrf-eth0
+ ip address 192.0.2.1/24
+!
+interface r1vrf-eth1
+ ip address 192.168.0.1/24
+ ipv6 address 192:168::1/64
+!
router bgp 65501 vrf vrf1
bgp router-id 192.168.0.1
bgp log-neighbor-changes
@@ -15,7 +22,6 @@ router bgp 65501 vrf vrf1
bmp monitor ipv6 unicast loc-rib
exit
!
-
address-family ipv4 unicast
neighbor 192.168.0.2 activate
neighbor 192.168.0.2 soft-reconfiguration inbound
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
index 038c87ca9..dc0228db6 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
index 6a7781377..6a7781377 100644
--- 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
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
index db3422014..64c8622ab 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
index 93f4a75e8..93f4a75e8 100644
--- 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
diff --git a/tests/topotests/bgp_bmp/r2/bgpd.conf b/tests/topotests/bgp_bmp/r2/frr.conf
index 40e2cd5bb..250071f48 100644
--- a/tests/topotests/bgp_bmp/r2/bgpd.conf
+++ b/tests/topotests/bgp_bmp/r2/frr.conf
@@ -1,3 +1,11 @@
+interface r2-eth0
+ ip address 192.168.0.2/24
+ ipv6 address 192:168::2/64
+!
+interface r2-eth1
+ ip address 172.31.0.2/24
+ ipv6 address 172:31::2/64
+!
router bgp 65502
bgp router-id 192.168.0.2
bgp log-neighbor-changes
diff --git a/tests/topotests/bgp_bmp/r2/zebra.conf b/tests/topotests/bgp_bmp/r2/zebra.conf
deleted file mode 100644
index 9d82bfe2d..000000000
--- a/tests/topotests/bgp_bmp/r2/zebra.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-interface r2-eth0
- ip address 192.168.0.2/24
- ipv6 address 192:168::2/64
-!
-interface r2-eth1
- ip address 172.31.0.2/24
- ipv6 address 172:31::2/64
-!
diff --git a/tests/topotests/bgp_bmp_vrf/r2/bgpd.conf b/tests/topotests/bgp_bmp/r2vrf/frr.conf
index 7c8255a17..5268190de 100644
--- a/tests/topotests/bgp_bmp_vrf/r2/bgpd.conf
+++ b/tests/topotests/bgp_bmp/r2vrf/frr.conf
@@ -1,3 +1,11 @@
+interface r2vrf-eth0
+ ip address 192.168.0.2/24
+ ipv6 address 192:168::2/64
+!
+interface r2vrf-eth1
+ ip address 172.31.0.2/24
+ ipv6 address 172:31::2/64
+!
router bgp 65502
bgp router-id 192.168.0.2
bgp log-neighbor-changes
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 658ad2b99..000000000
--- 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 <farid.mihoub@6wind.com>
-#
-
-"""
-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:<vrf_id>.
- # 0:<vrf_id> 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 000000000..614286344
--- /dev/null
+++ b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+
+"""
+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:
+ pcap_file = os.path.join(tgen.logdir, "r1/bmp.pcap")
+ tgen.gears["r1"].run(
+ "tcpdump -nni r1-eth0 -s 0 -w {} &".format(pcap_file), stdout=None
+ )
+
+ for rname, router in tgen.routers().items():
+ logger.info("Loading router %s" % rname)
+ router.load_frr_config(
+ os.path.join(CWD, "{}/frr.conf".format(rname)),
+ [(TopoRouter.RD_ZEBRA, None), (TopoRouter.RD_BGP, "-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"),
+ 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 000000000..b45452e7c
--- /dev/null
+++ b/tests/topotests/bgp_bmp/test_bgp_bmp_2.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+
+"""
+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,
+ bmp_reset_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
+"""
+ )
+ bmp_reset_seq()
+ if DEBUG_PCAP:
+ pcap_file = os.path.join(tgen.logdir, "r1vrf/bmp.pcap")
+ tgen.gears["r1vrf"].run(
+ "tcpdump -nni r1vrf-eth0 -s 0 -w {} &".format(pcap_file), stdout=None
+ )
+
+ for rname, router in tgen.routers().items():
+ logger.info("Loading router %s" % rname)
+ router.load_frr_config(
+ os.path.join(CWD, "{}/frr.conf".format(rname)),
+ [(TopoRouter.RD_ZEBRA, None), (TopoRouter.RD_BGP, "-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"),
+ 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 e69de29bb..000000000
--- a/tests/topotests/bgp_bmp_vrf/__init__.py
+++ /dev/null
diff --git a/tests/topotests/bgp_bmp_vrf/r1/zebra.conf b/tests/topotests/bgp_bmp_vrf/r1/zebra.conf
deleted file mode 100644
index 0b523c9e1..000000000
--- a/tests/topotests/bgp_bmp_vrf/r1/zebra.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-interface r1-eth0
- ip address 192.0.2.1/24
-!
-interface r1-eth1
- ip address 192.168.0.1/24
- ipv6 address 192:168::1/64
-!
diff --git a/tests/topotests/bgp_bmp_vrf/r2/zebra.conf b/tests/topotests/bgp_bmp_vrf/r2/zebra.conf
deleted file mode 100644
index 9d82bfe2d..000000000
--- a/tests/topotests/bgp_bmp_vrf/r2/zebra.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-interface r2-eth0
- ip address 192.168.0.2/24
- ipv6 address 192:168::2/64
-!
-interface r2-eth1
- ip address 172.31.0.2/24
- ipv6 address 172:31::2/64
-!
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 d31328bdb..000000000
--- 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 <farid.mihoub@6wind.com>
-#
-
-"""
-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:<vrf_id>.
- # 0:<vrf_id> 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 bcd1c7481..329c2b54f 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",
+ f"router bgp {asn}{vrf}\n"
+ f"address-family ipv{ip.version} {safi}\n"
+ f"{withdraw}network {ip}\n".format(withdraw, ip),
+ "exit-address-family\n",
+ ]
+ logger.debug(f"setting prefix: ipv{ip.version} {safi} {ip}")
+ router.vtysh_cmd("".join(cmd))
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py b/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py
index 3694cb4fe..ca49c405d 100644
--- a/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py
+++ b/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py
@@ -72,6 +72,12 @@ class PathAttribute:
if path_attr_cls == cls.UNKNOWN_ATTR:
return data[offset + attr_len :], None
+ # RFC1771, 4.3 UPDATE Message Format
+ # The path segment length is a 1-octet long field containing
+ # the number of ASs in the path segment value field.
+ if type_code == PATH_ATTR_TYPE_AS_PATH and attr_len == 0:
+ return data[offset:], path_attr_cls.dissect(data[offset : offset + 2])
+
return data[offset + attr_len :], path_attr_cls.dissect(
data[offset : offset + attr_len]
)
diff --git a/tests/topotests/lib/bmp_collector/bmpserver b/tests/topotests/lib/bmp_collector/bmpserver.py
index 56d85fc74..c42c38756 100755
--- a/tests/topotests/lib/bmp_collector/bmpserver
+++ b/tests/topotests/lib/bmp_collector/bmpserver.py
@@ -5,8 +5,11 @@
# Authored by Farid Mihoub <farid.mihoub@6wind.com>
#
import argparse
+import errno
+import logging
# XXX: something more reliable should be used "Twisted" a great choice.
+import os
import signal
import socket
import sys
@@ -20,11 +23,11 @@ BGP_MAX_SIZE = 4096
# Global variable to track shutdown signal
shutdown = False
-
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--address", type=str, default="0.0.0.0")
parser.add_argument("-p", "--port", type=int, default=1789)
parser.add_argument("-l", "--logfile", type=str, default="/var/log/bmp.log")
+parser.add_argument("-r", "--pidfile", type=str, default="/var/run/bmp.pid")
def handle_signal(signum, frame):
@@ -40,6 +43,74 @@ def timestamp_print(message, file=sys.stderr):
print(f"[{current_time}] {message}", file=file)
+def check_pid(pid):
+ if pid < 0: # user input error
+ return False
+ if pid == 0: # all processes
+ return False
+ try:
+ os.kill(pid, 0)
+ return True
+ except OSError as err:
+ if err.errno == errno.EPERM: # a process we were denied access to
+ return True
+ if err.errno == errno.ESRCH: # No such process
+ return False
+ # should never happen
+ return False
+
+
+def savepid():
+ ownid = os.getpid()
+
+ flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
+ mode = ((os.R_OK | os.W_OK) << 6) | (os.R_OK << 3) | os.R_OK
+
+ try:
+ fd = os.open(pid_file, flags, mode)
+ except OSError:
+ try:
+ pid = open(pid_file, "r").readline().strip()
+ if check_pid(int(pid)):
+ timestamp_print(
+ "PID file already exists and program still running %s\n" % pid_file
+ )
+ return False
+ else:
+ # If pid is not running, reopen file without O_EXCL
+ fd = os.open(pid_file, flags ^ os.O_EXCL, mode)
+ except (OSError, IOError, ValueError):
+ timestamp_print(
+ "issue accessing PID file %s (most likely permission or ownership)\n"
+ % pid_file
+ )
+ return False
+
+ try:
+ f = os.fdopen(fd, "w")
+ line = "%d\n" % ownid
+ f.write(line)
+ f.close()
+ saved_pid = True
+ except IOError:
+ timestamp_print("Can not create PID file %s\n" % pid_file)
+ return False
+ timestamp_print("Created PID file %s with value %d\n" % (pid_file, ownid))
+ return True
+
+
+def removepid():
+ try:
+ os.remove(pid_file)
+ except OSError as exc:
+ if exc.errno == errno.ENOENT:
+ pass
+ else:
+ timestamp_print("Can not remove PID file %s\n" % pid_file)
+ return
+ timestamp_print("Removed PID file %s\n" % pid_file)
+
+
def main():
global shutdown
@@ -51,8 +122,13 @@ def main():
ADDRESS, PORT = args.address, args.port
LOG_FILE = args.logfile
+ global pid_file
+ pid_file = args.pidfile
+
timestamp_print(f"Starting bmpserver on {args.address}:{args.port}")
+ savepid()
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
@@ -80,9 +156,7 @@ def main():
while len(data) > BMPMsg.MIN_LEN:
data = BMPMsg.dissect(data, log_file=LOG_FILE)
- timestamp_print(
- f"Finished dissecting data from {client_address}"
- )
+ timestamp_print(f"Finished dissecting data from {client_address}")
except Exception as e:
timestamp_print(f"{e}")
@@ -99,6 +173,7 @@ def main():
timestamp_print(f"{e}")
finally:
timestamp_print(f"Server shutting down on {ADDRESS}:{PORT}")
+ removepid()
if __name__ == "__main__":
@@ -106,4 +181,5 @@ if __name__ == "__main__":
sys.exit(main())
except KeyboardInterrupt:
logging.info("BMP server was interrupted and is shutting down.")
+ removepid()
sys.exit(0)
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index 4d7c56423..0a9a84a4b 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -1293,18 +1293,19 @@ class TopoBMPCollector(TopoHost):
log_err = os.path.join(log_dir, "bmpserver.log")
log_arg = "-l {}".format(log_file) if log_file else ""
+ self.pid_file = os.path.join(log_dir, "bmpserver.pid")
with open(log_err, "w") as err:
self.run(
- "{}/bmp_collector/bmpserver -a {} -p {} {}&".format(
- CWD, self.ip, self.port, log_arg
+ "{}/bmp_collector/bmpserver.py -a {} -p {} -r {} {}&".format(
+ CWD, self.ip, self.port, self.pid_file, log_arg
),
stdout=None,
stderr=err,
)
def stop(self):
- self.run("pkill -f bmpserver")
+ self.run(f"kill $(cat {self.pid_file}")
return ""