Skip to content

Commit 6f88517

Browse files
committed
topotest: add bgp_bmp_vrf topotest
Add test to check BMP in VRF. Note that the following configuration works with interface r1-eth0 towards 192.0.2.10 (BMP collector) in the default VRF but not in vrf1. > router bgp 65501 vrf vrf1 > bmp targets bmp1 > bmp connect 192.0.2.10 port 1789 min-retry 100 max-retry 10000 Also, for some reasons, the test works even without "bgpd: bmp loc-rib peer up/down for vrfs" commit. Signed-off-by: Louis Scalbert <[email protected]>
1 parent 7ea761f commit 6f88517

File tree

6 files changed

+383
-0
lines changed

6 files changed

+383
-0
lines changed

tests/topotests/bgp_bmp_vrf/__init__.py

Whitespace-only changes.
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
router bgp 65501 vrf vrf1
2+
bgp router-id 192.168.0.1
3+
bgp log-neighbor-changes
4+
no bgp ebgp-requires-policy
5+
neighbor 192.168.0.2 remote-as 65502
6+
neighbor 192:168::2 remote-as 65502
7+
!
8+
bmp targets bmp1
9+
bmp connect 192.0.2.10 port 1789 min-retry 100 max-retry 10000
10+
exit
11+
!
12+
13+
address-family ipv4 unicast
14+
neighbor 192.168.0.2 activate
15+
neighbor 192.168.0.2 soft-reconfiguration inbound
16+
no neighbor 192:168::2 activate
17+
exit-address-family
18+
!
19+
address-family ipv6 unicast
20+
neighbor 192:168::2 activate
21+
neighbor 192:168::2 soft-reconfiguration inbound
22+
exit-address-family
23+
!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
interface r1-eth0
2+
ip address 192.0.2.1/24
3+
!
4+
interface r1-eth1
5+
ip address 192.168.0.1/24
6+
ipv6 address 192:168::1/64
7+
!
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
router bgp 65502
2+
bgp router-id 192.168.0.2
3+
bgp log-neighbor-changes
4+
no bgp ebgp-requires-policy
5+
no bgp network import-check
6+
neighbor 192.168.0.1 remote-as 65501
7+
neighbor 192:168::1 remote-as 65501
8+
!
9+
address-family ipv4 unicast
10+
neighbor 192.168.0.1 activate
11+
no neighbor 192:168::1 activate
12+
redistribute connected
13+
exit-address-family
14+
!
15+
address-family ipv6 unicast
16+
neighbor 192:168::1 activate
17+
redistribute connected
18+
exit-address-family
19+
!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
interface r2-eth0
2+
ip address 192.168.0.2/24
3+
ipv6 address 192:168::2/64
4+
!
5+
interface r2-eth1
6+
ip address 172.31.0.2/24
7+
ipv6 address 172:31::2/64
8+
!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
#!/usr/bin/env python
2+
# SPDX-License-Identifier: ISC
3+
4+
# Copyright 2023 6WIND S.A.
5+
# Authored by Farid Mihoub <[email protected]>
6+
#
7+
8+
"""
9+
test_bgp_bmp.py: Test BGP BMP functionalities
10+
11+
+------+ +------+ +------+
12+
| | | | | |
13+
| BMP1 |------------| R1 |---------------| R2 |
14+
| | | | | |
15+
+------+ +------+ +------+
16+
17+
Setup two routers R1 and R2 with one link configured with IPv4 and
18+
IPv6 addresses.
19+
Configure BGP in R1 and R2 to exchange prefixes from
20+
the latter to the first router.
21+
Setup a link between R1 and the BMP server, activate the BMP feature in R1
22+
and ensure the monitored BGP sessions logs are well present on the BMP server.
23+
"""
24+
25+
from functools import partial
26+
from ipaddress import ip_network
27+
import json
28+
import os
29+
import platform
30+
import pytest
31+
import sys
32+
33+
# Save the Current Working Directory to find configuration files.
34+
CWD = os.path.dirname(os.path.realpath(__file__))
35+
sys.path.append(os.path.join("../"))
36+
sys.path.append(os.path.join("../lib/"))
37+
38+
# pylint: disable=C0413
39+
# Import topogen and topotest helpers
40+
from lib import topotest
41+
from lib.bgp import verify_bgp_convergence_from_running_config
42+
from lib.topogen import Topogen, TopoRouter, get_topogen
43+
from lib.topolog import logger
44+
45+
pytestmark = [pytest.mark.bgpd]
46+
47+
# remember the last sequence number of the logging messages
48+
SEQ = 0
49+
50+
PRE_POLICY = "pre-policy"
51+
POST_POLICY = "post-policy"
52+
LOC_RIB = "loc-rib"
53+
54+
55+
def build_topo(tgen):
56+
tgen.add_router("r1")
57+
tgen.add_router("r2")
58+
tgen.add_bmp_server("bmp1", ip="192.0.2.10", defaultRoute="via 192.0.2.1")
59+
60+
switch = tgen.add_switch("s1")
61+
switch.add_link(tgen.gears["r1"])
62+
switch.add_link(tgen.gears["bmp1"])
63+
64+
tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth1", "r2-eth0")
65+
66+
67+
def setup_module(mod):
68+
tgen = Topogen(build_topo, mod.__name__)
69+
tgen.start_topology()
70+
71+
tgen.net["r1"].cmd(
72+
"""
73+
ip link add vrf1 type vrf table 10
74+
ip link set vrf1 up
75+
ip link set r1-eth1 master vrf1
76+
"""
77+
)
78+
79+
for rname, router in tgen.routers().items():
80+
router.load_config(
81+
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
82+
)
83+
router.load_config(
84+
TopoRouter.RD_BGP,
85+
os.path.join(CWD, "{}/bgpd.conf".format(rname)),
86+
"-M bmp",
87+
)
88+
89+
tgen.start_router()
90+
91+
logger.info("starting BMP servers")
92+
for bmp_name, server in tgen.get_bmp_servers().items():
93+
server.start(log_file=os.path.join(tgen.logdir, bmp_name, "bmp.log"))
94+
95+
96+
def teardown_module(_mod):
97+
tgen = get_topogen()
98+
tgen.stop_topology()
99+
100+
101+
def test_bgp_convergence():
102+
tgen = get_topogen()
103+
if tgen.routers_have_failure():
104+
pytest.skip(tgen.errors)
105+
106+
result = verify_bgp_convergence_from_running_config(tgen, dut="r1")
107+
assert result is True, "BGP is not converging"
108+
109+
110+
def get_bmp_messages():
111+
"""
112+
Read the BMP logging messages.
113+
"""
114+
messages = []
115+
tgen = get_topogen()
116+
text_output = tgen.gears["bmp1"].run(
117+
"cat {}".format(os.path.join(tgen.logdir, "bmp1", "bmp.log"))
118+
)
119+
120+
for m in text_output.splitlines():
121+
# some output in the bash can break the message decoding
122+
try:
123+
messages.append(json.loads(m))
124+
except Exception as e:
125+
logger.warning(str(e) + " message: {}".format(str(m)))
126+
continue
127+
128+
if not messages:
129+
logger.error("Bad BMP log format, check your BMP server")
130+
131+
return messages
132+
133+
134+
def check_for_prefixes(expected_prefixes, bmp_log_type, policy, labels=None):
135+
"""
136+
Check for the presence of the given prefixes in the BMP server logs with
137+
the given message type and the set policy.
138+
"""
139+
global SEQ
140+
# we care only about the new messages
141+
messages = [
142+
m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ
143+
]
144+
145+
# get the list of pairs (prefix, policy, seq) for the given message type
146+
prefixes = [
147+
m["ip_prefix"]
148+
for m in messages
149+
if "ip_prefix" in m.keys()
150+
and "bmp_log_type" in m.keys()
151+
and m["bmp_log_type"] == bmp_log_type
152+
and m["policy"] == policy
153+
and (
154+
labels is None
155+
or (
156+
m["ip_prefix"] in labels.keys() and m["label"] == labels[m["ip_prefix"]]
157+
)
158+
)
159+
]
160+
161+
# check for prefixes
162+
for ep in expected_prefixes:
163+
if ep not in prefixes:
164+
msg = "The prefix {} is not present in the {} log messages."
165+
logger.debug(msg.format(ep, bmp_log_type))
166+
return False
167+
168+
SEQ = messages[-1]["seq"]
169+
return True
170+
171+
172+
def check_for_peer_message(expected_peers, bmp_log_type):
173+
"""
174+
Check for the presence of a peer up message for the peer
175+
"""
176+
global SEQ
177+
# we care only about the new messages
178+
messages = [
179+
m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ
180+
]
181+
182+
# get the list of pairs (prefix, policy, seq) for the given message type
183+
peers = [
184+
m["peer_ip"]
185+
for m in messages
186+
if "peer_ip" in m.keys() and m["bmp_log_type"] == bmp_log_type
187+
]
188+
189+
# check for prefixes
190+
for ep in expected_peers:
191+
if ep not in peers:
192+
msg = "The peer {} is not present in the {} log messages."
193+
logger.debug(msg.format(ep, bmp_log_type))
194+
return False
195+
196+
SEQ = messages[-1]["seq"]
197+
return True
198+
199+
200+
def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None):
201+
"""
202+
Configure the bmp policy.
203+
"""
204+
vrf = " vrf {}".format(vrf) if vrf else ""
205+
cmd = [
206+
"con t\n",
207+
"router bgp {}{}\n".format(asn, vrf),
208+
"bmp targets {}\n".format(target),
209+
"bmp monitor ipv4 {} {}\n".format(safi, policy),
210+
"bmp monitor ipv6 {} {}\n".format(safi, policy),
211+
"end\n",
212+
]
213+
tgen.gears[node].vtysh_cmd("".join(cmd))
214+
215+
216+
def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True):
217+
"""
218+
Configure the bgp prefixes.
219+
"""
220+
withdraw = "no " if not update else ""
221+
vrf = " vrf {}".format(vrf) if vrf else ""
222+
for p in prefixes:
223+
ip = ip_network(p)
224+
cmd = [
225+
"conf t\n",
226+
"router bgp {}{}\n".format(asn, vrf),
227+
"address-family ipv{} {}\n".format(ip.version, safi),
228+
"{}network {}\n".format(withdraw, ip),
229+
"exit-address-family\n",
230+
]
231+
logger.debug("setting prefix: ipv{} {} {}".format(ip.version, safi, ip))
232+
tgen.gears[node].vtysh_cmd("".join(cmd))
233+
234+
235+
def unicast_prefixes(policy):
236+
"""
237+
Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes.
238+
Check if the previous actions are logged in the BMP server with the right
239+
message type and the right policy.
240+
"""
241+
tgen = get_topogen()
242+
set_bmp_policy(tgen, "r1", 65501, "bmp1", "unicast", policy, vrf="vrf1")
243+
244+
prefixes = ["172.31.0.15/32", "2111::1111/128"]
245+
# add prefixes
246+
configure_prefixes(tgen, "r2", 65502, "unicast", prefixes)
247+
248+
logger.info("checking for updated prefixes")
249+
# check
250+
test_func = partial(check_for_prefixes, prefixes, "update", policy)
251+
success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
252+
assert success, "Checking the updated prefixes has been failed !."
253+
254+
# withdraw prefixes
255+
configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False)
256+
logger.info("checking for withdrawed prefxies")
257+
# check
258+
test_func = partial(check_for_prefixes, prefixes, "withdraw", policy)
259+
success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
260+
assert success, "Checking the withdrawed prefixes has been failed !."
261+
262+
263+
def test_bmp_server_logging():
264+
"""
265+
Assert the logging of the bmp server.
266+
"""
267+
268+
def check_for_log_file():
269+
tgen = get_topogen()
270+
output = tgen.gears["bmp1"].run(
271+
"ls {}".format(os.path.join(tgen.logdir, "bmp1"))
272+
)
273+
if "bmp.log" not in output:
274+
return False
275+
return True
276+
277+
success, _ = topotest.run_and_expect(check_for_log_file, True, wait=0.5)
278+
assert success, "The BMP server is not logging"
279+
280+
281+
def test_peer_up():
282+
"""
283+
Checking for BMP peers up messages
284+
"""
285+
286+
peers = ["192.168.0.2", "192:168::2"]
287+
288+
logger.info("checking for BMP peers up messages")
289+
290+
test_func = partial(check_for_peer_message, peers, "peer up")
291+
success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
292+
assert success, "Checking the updated prefixes has been failed !."
293+
294+
295+
def test_bmp_bgp_unicast():
296+
"""
297+
Add/withdraw bgp unicast prefixes and check the bmp logs.
298+
"""
299+
logger.info("*** Unicast prefixes pre-policy logging ***")
300+
unicast_prefixes(PRE_POLICY)
301+
logger.info("*** Unicast prefixes post-policy logging ***")
302+
unicast_prefixes(POST_POLICY)
303+
logger.info("*** Unicast prefixes loc-rib logging ***")
304+
unicast_prefixes(LOC_RIB)
305+
306+
307+
def test_peer_down():
308+
"""
309+
Checking for BMP peers down messages
310+
"""
311+
tgen = get_topogen()
312+
313+
tgen.gears["r2"].vtysh_cmd("clear bgp *")
314+
315+
peers = ["192.168.0.2", "192:168::2"]
316+
317+
logger.info("checking for BMP peers down messages")
318+
319+
test_func = partial(check_for_peer_message, peers, "peer down")
320+
success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
321+
assert success, "Checking the updated prefixes has been failed !."
322+
323+
324+
if __name__ == "__main__":
325+
args = ["-s"] + sys.argv[1:]
326+
sys.exit(pytest.main(args))

0 commit comments

Comments
 (0)