From ca2b13fd5e07852a912e72a78ca568fcbda61bb4 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 31 Mar 2023 10:11:52 +0200 Subject: [PATCH] topotests: add bgp test for backup routes There is no test that illustrate how BGP can handle backup routes. This test creates a core topology with multiple P devices, and CEs devices. The ce9 device is reachable via rt11, rt5 and rt6, while the ce7 device is reachable via rt1. The expectation is to test incoming routes on rt1. The test does the following: - at startup, backup routes support is configured, and 2 ECMP routes, and 1 backup path should be present. - the IGP metric of rt6 is set to 10: the route to rt6 becomes ECMP, like the other 2. - the IGP metric of rt5 and rt11 is set to 20: the route to rt6 becomes primary, and the other two are backup and backup-multipath - the unconfiguration of addpath backup support should remove the backup and backup-multipath - the reconfiguraiton of addpath backup support should add the backup and backup-multipath - the test of the addpath-tx-backup functionality. The below output can be visible on rt1 during the tests: > rt1# show bgp ipv4 > BGP table version is 2, local router ID is 192.0.2.1, vrf id 0 > Default local pref 100, local AS 64500 > Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, > i internal, r RIB-failure, S Stale, R Removed > Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self > Origin codes: i - IGP, e - EGP, ? - incomplete > RPKI validation codes: V valid, I invalid, N Not found > > Network Next Hop Metric LocPrf Weight Path > *>i192.0.2.9/32 192.0.2.5 0 100 0 64511 i > b i 192.0.2.6 0 100 0 64511 i > *>i192.0.2.89/32 192.0.2.2 0 100 0 64499 i > Signed-off-by: Philippe Guibert --- tests/topotests/bgp_backup_route/__init__.py | 0 .../topotests/bgp_backup_route/ce7/bgpd.conf | 8 + .../topotests/bgp_backup_route/ce7/zebra.conf | 10 + .../topotests/bgp_backup_route/ce9/bgpd.conf | 11 + .../topotests/bgp_backup_route/ce9/zebra.conf | 16 + tests/topotests/bgp_backup_route/r1/bgpd.conf | 16 + .../topotests/bgp_backup_route/r1/isisd.conf | 22 + .../topotests/bgp_backup_route/r1/zebra.conf | 17 + .../topotests/bgp_backup_route/r10/isisd.conf | 30 + .../topotests/bgp_backup_route/r10/zebra.conf | 17 + .../topotests/bgp_backup_route/r11/bgpd.conf | 13 + .../topotests/bgp_backup_route/r11/isisd.conf | 26 + .../topotests/bgp_backup_route/r11/zebra.conf | 17 + tests/topotests/bgp_backup_route/r3/bgpd.conf | 12 + .../topotests/bgp_backup_route/r3/isisd.conf | 38 + .../topotests/bgp_backup_route/r3/zebra.conf | 19 + .../topotests/bgp_backup_route/r4/isisd.conf | 30 + .../topotests/bgp_backup_route/r4/zebra.conf | 14 + tests/topotests/bgp_backup_route/r5/bgpd.conf | 13 + .../topotests/bgp_backup_route/r5/isisd.conf | 26 + .../topotests/bgp_backup_route/r5/zebra.conf | 20 + tests/topotests/bgp_backup_route/r6/bgpd.conf | 13 + .../topotests/bgp_backup_route/r6/isisd.conf | 23 + .../topotests/bgp_backup_route/r6/zebra.conf | 14 + .../bgp_backup_route/test_bgp_backup_route.py | 707 ++++++++++++++++++ 25 files changed, 1132 insertions(+) create mode 100644 tests/topotests/bgp_backup_route/__init__.py create mode 100644 tests/topotests/bgp_backup_route/ce7/bgpd.conf create mode 100644 tests/topotests/bgp_backup_route/ce7/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/ce9/bgpd.conf create mode 100644 tests/topotests/bgp_backup_route/ce9/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/r1/bgpd.conf create mode 100644 tests/topotests/bgp_backup_route/r1/isisd.conf create mode 100644 tests/topotests/bgp_backup_route/r1/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/r10/isisd.conf create mode 100644 tests/topotests/bgp_backup_route/r10/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/r11/bgpd.conf create mode 100644 tests/topotests/bgp_backup_route/r11/isisd.conf create mode 100644 tests/topotests/bgp_backup_route/r11/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/r3/bgpd.conf create mode 100644 tests/topotests/bgp_backup_route/r3/isisd.conf create mode 100644 tests/topotests/bgp_backup_route/r3/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/r4/isisd.conf create mode 100644 tests/topotests/bgp_backup_route/r4/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/r5/bgpd.conf create mode 100644 tests/topotests/bgp_backup_route/r5/isisd.conf create mode 100644 tests/topotests/bgp_backup_route/r5/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/r6/bgpd.conf create mode 100644 tests/topotests/bgp_backup_route/r6/isisd.conf create mode 100644 tests/topotests/bgp_backup_route/r6/zebra.conf create mode 100644 tests/topotests/bgp_backup_route/test_bgp_backup_route.py diff --git a/tests/topotests/bgp_backup_route/__init__.py b/tests/topotests/bgp_backup_route/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_backup_route/ce7/bgpd.conf b/tests/topotests/bgp_backup_route/ce7/bgpd.conf new file mode 100644 index 000000000000..fa09771669b7 --- /dev/null +++ b/tests/topotests/bgp_backup_route/ce7/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 64499 + bgp router-id 192.0.2.7 + no bgp ebgp-requires-policy + neighbor 172.31.10.1 remote-as 64500 + address-family ipv4 unicast + neighbor 172.31.10.1 addpath-tx-all-paths +! + diff --git a/tests/topotests/bgp_backup_route/ce7/zebra.conf b/tests/topotests/bgp_backup_route/ce7/zebra.conf new file mode 100644 index 000000000000..ec2069663830 --- /dev/null +++ b/tests/topotests/bgp_backup_route/ce7/zebra.conf @@ -0,0 +1,10 @@ +log stdout +interface lo + ip address 192.0.2.7/32 +! +interface loop2 + ip address 192.0.2.89/32 +! +interface ce7-eth0 + ip address 172.31.10.7/24 +! diff --git a/tests/topotests/bgp_backup_route/ce9/bgpd.conf b/tests/topotests/bgp_backup_route/ce9/bgpd.conf new file mode 100644 index 000000000000..a545a652cd21 --- /dev/null +++ b/tests/topotests/bgp_backup_route/ce9/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 64511 + bgp router-id 192.0.2.7 + no bgp ebgp-requires-policy + neighbor 172.31.12.5 remote-as 64500 + neighbor 172.31.13.6 remote-as 64500 + neighbor 172.31.20.11 remote-as 64500 + address-family ipv4 unicast + network 192.0.2.9/32 + ! +! + diff --git a/tests/topotests/bgp_backup_route/ce9/zebra.conf b/tests/topotests/bgp_backup_route/ce9/zebra.conf new file mode 100644 index 000000000000..2546ca47d24f --- /dev/null +++ b/tests/topotests/bgp_backup_route/ce9/zebra.conf @@ -0,0 +1,16 @@ +log stdout +interface lo + ip address 192.0.2.9/32 +! +interface loop2 + ip address 192.0.2.9/32 +! +interface ce9-eth0 + ip address 172.31.12.9/24 +! +interface ce9-eth1 + ip address 172.31.13.9/24 +! +interface ce9-eth2 + ip address 172.31.20.9/24 +! diff --git a/tests/topotests/bgp_backup_route/r1/bgpd.conf b/tests/topotests/bgp_backup_route/r1/bgpd.conf new file mode 100644 index 000000000000..65502888f759 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r1/bgpd.conf @@ -0,0 +1,16 @@ +debug bgp updates out +router bgp 64500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + neighbor 172.31.10.7 remote-as 64499 + neighbor rrserver peer-group + neighbor rrserver remote-as 64500 + neighbor rrserver update-source lo + neighbor 192.0.2.3 peer-group rrserver + address-family ipv4 unicast + addpath path-selection backup + neighbor rrserver next-hop-self + neighbor rrserver addpath-tx-all-paths + neighbor 172.31.10.7 addpath-tx-all-paths + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_backup_route/r1/isisd.conf b/tests/topotests/bgp_backup_route/r1/isisd.conf new file mode 100644 index 000000000000..935ab90419f4 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r1/isisd.conf @@ -0,0 +1,22 @@ +hostname r1 +interface lo + ip router isis 1 + isis passive +! +interface r1-eth1 + ip router isis 1 + isis network point-to-point +! +interface r1-eth2 + ip router isis 1 + isis network point-to-point +! +router isis 1 + net 49.0123.6452.0001.00 + is-type level-2-only + mpls-te on + segment-routing on + segment-routing global-block 16000 17000 + segment-routing node-msd 10 + segment-routing prefix 192.0.2.1/32 index 1 +! diff --git a/tests/topotests/bgp_backup_route/r1/zebra.conf b/tests/topotests/bgp_backup_route/r1/zebra.conf new file mode 100644 index 000000000000..bd4c1b51d859 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r1/zebra.conf @@ -0,0 +1,17 @@ +log stdout +interface lo + ip address 192.0.2.1/32 +! +interface r1-eth0 + ip address 172.31.10.1/24 +! +interface r1-eth1 + ip address 172.31.0.1/24 +! +interface r1-eth2 + ip address 172.31.2.1/24 +! +interface r1-eth3 + ip address 172.31.15.1/24 +! + diff --git a/tests/topotests/bgp_backup_route/r10/isisd.conf b/tests/topotests/bgp_backup_route/r10/isisd.conf new file mode 100644 index 000000000000..f79488c8e3c0 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r10/isisd.conf @@ -0,0 +1,30 @@ +hostname r10 +interface lo + ip router isis 1 + isis passive +! +interface r10-eth0 + ip router isis 1 + isis network point-to-point +! +interface r10-eth1 + ip router isis 1 + isis network point-to-point +! +interface r10-eth2 + ip router isis 1 + isis network point-to-point +! +interface r10-eth3 + ip router isis 1 + isis network point-to-point +! +router isis 1 + net 49.0123.6452.0010.00 + is-type level-2-only + mpls-te on + segment-routing on + segment-routing global-block 16000 17000 + segment-routing node-msd 10 + segment-routing prefix 192.0.2.10/32 index 10 +! diff --git a/tests/topotests/bgp_backup_route/r10/zebra.conf b/tests/topotests/bgp_backup_route/r10/zebra.conf new file mode 100644 index 000000000000..03384d0739da --- /dev/null +++ b/tests/topotests/bgp_backup_route/r10/zebra.conf @@ -0,0 +1,17 @@ +log stdout +interface lo + ip address 192.0.2.10/32 +! +interface r10-eth0 + ip address 172.31.14.10/24 +! +interface r10-eth1 + ip address 172.31.15.10/24 +! +interface r10-eth2 + ip address 172.31.16.10/24 +! +interface r10-eth3 + ip address 172.31.17.10/24 +! + diff --git a/tests/topotests/bgp_backup_route/r11/bgpd.conf b/tests/topotests/bgp_backup_route/r11/bgpd.conf new file mode 100644 index 000000000000..8b5c704ce947 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r11/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 64500 + bgp router-id 192.0.2.11 + no bgp ebgp-requires-policy + neighbor 172.31.20.9 remote-as 64511 + neighbor rrserver peer-group + neighbor rrserver remote-as 64500 + neighbor rrserver update-source lo + neighbor 192.0.2.3 peer-group rrserver + address-family ipv4 unicast + neighbor rrserver next-hop-self + neighbor rrserver addpath-tx-all-paths + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_backup_route/r11/isisd.conf b/tests/topotests/bgp_backup_route/r11/isisd.conf new file mode 100644 index 000000000000..caed65032097 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r11/isisd.conf @@ -0,0 +1,26 @@ +hostname r11 +interface lo + ip router isis 1 + isis passive +! +interface r11-eth0 + ip router isis 1 + isis network point-to-point +! +interface r11-eth1 + ip router isis 1 + isis network point-to-point +! +interface r11-eth2 + ip router isis 1 + isis network point-to-point +! +router isis 1 + net 49.0123.6452.0011.00 + is-type level-2-only + mpls-te on + segment-routing on + segment-routing global-block 16000 17000 + segment-routing node-msd 10 + segment-routing prefix 192.0.2.11/32 index 11 +! diff --git a/tests/topotests/bgp_backup_route/r11/zebra.conf b/tests/topotests/bgp_backup_route/r11/zebra.conf new file mode 100644 index 000000000000..b4b9bbbf430e --- /dev/null +++ b/tests/topotests/bgp_backup_route/r11/zebra.conf @@ -0,0 +1,17 @@ +log stdout +interface lo + ip address 192.0.2.11/32 +! +interface r11-eth0 + ip address 172.31.17.11/24 +! +interface r11-eth1 + ip address 172.31.18.11/24 +! +interface r11-eth2 + ip address 172.31.19.11/24 +! +interface r11-eth3 + ip address 172.31.20.11/24 +! + diff --git a/tests/topotests/bgp_backup_route/r3/bgpd.conf b/tests/topotests/bgp_backup_route/r3/bgpd.conf new file mode 100644 index 000000000000..b215fb24aac9 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r3/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 64500 + bgp router-id 192.0.2.3 + neighbor rr peer-group + neighbor rr remote-as 64500 + neighbor rr update-source lo + bgp listen range 192.0.2.0/24 peer-group rr + ! + address-family ipv4 unicast + neighbor rr addpath-tx-all-paths + neighbor rr route-reflector-client + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_backup_route/r3/isisd.conf b/tests/topotests/bgp_backup_route/r3/isisd.conf new file mode 100644 index 000000000000..ae6bddee9208 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r3/isisd.conf @@ -0,0 +1,38 @@ +hostname r3 +interface lo + ip router isis 1 + isis passive +! +interface r3-eth0 + ip router isis 1 + isis network point-to-point +! +interface r3-eth1 + ip router isis 1 + isis network point-to-point +! +interface r3-eth2 + ip router isis 1 + isis network point-to-point +! +interface r3-eth3 + ip router isis 1 + isis network point-to-point +! +interface r3-eth4 + ip router isis 1 + isis network point-to-point +! +interface r3-eth5 + ip router isis 1 + isis network point-to-point +! +router isis 1 + net 49.0123.6452.0003.00 + is-type level-2-only + mpls-te on + segment-routing on + segment-routing global-block 16000 17000 + segment-routing node-msd 10 + segment-routing prefix 192.0.2.3/32 index 3 +! diff --git a/tests/topotests/bgp_backup_route/r3/zebra.conf b/tests/topotests/bgp_backup_route/r3/zebra.conf new file mode 100644 index 000000000000..dadfaedcf8a7 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r3/zebra.conf @@ -0,0 +1,19 @@ +log stdout +interface lo + ip address 192.0.2.3/32 +! +interface r3-eth0 + ip address 172.31.0.3/24 +! +interface r3-eth1 + ip address 172.31.4.3/24 +! +interface r3-eth2 + ip address 172.31.5.3/24 +! +interface r3-eth3 + ip address 172.31.14.3/24 +! +interface r3-eth4 + ip address 172.31.18.3/24 +! diff --git a/tests/topotests/bgp_backup_route/r4/isisd.conf b/tests/topotests/bgp_backup_route/r4/isisd.conf new file mode 100644 index 000000000000..4d49d0de0a45 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r4/isisd.conf @@ -0,0 +1,30 @@ +hostname r4 +interface lo + ip router isis 1 + isis passive +! +interface r4-eth0 + ip router isis 1 + isis network point-to-point +! +interface r4-eth1 + ip router isis 1 + isis network point-to-point +! +interface r4-eth2 + ip router isis 1 + isis network point-to-point +! +interface r4-eth3 + ip router isis 1 + isis network point-to-point +! +router isis 1 + net 49.0123.6452.0004.00 + is-type level-2-only + mpls-te on + segment-routing on + segment-routing global-block 16000 17000 + segment-routing node-msd 10 + segment-routing prefix 192.0.2.4/32 index 4 +! diff --git a/tests/topotests/bgp_backup_route/r4/zebra.conf b/tests/topotests/bgp_backup_route/r4/zebra.conf new file mode 100644 index 000000000000..5192a4ce0c20 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r4/zebra.conf @@ -0,0 +1,14 @@ +log stdout +interface lo + ip address 192.0.2.4/32 +! +interface r4-eth0 + ip address 172.31.2.4/24 +! +interface r4-eth1 + ip address 172.31.1.4/24 +! +interface r4-eth2 + ip address 172.31.7.4/24 +! + diff --git a/tests/topotests/bgp_backup_route/r5/bgpd.conf b/tests/topotests/bgp_backup_route/r5/bgpd.conf new file mode 100644 index 000000000000..f9294f4c6020 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r5/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 64500 + bgp router-id 192.0.2.5 + no bgp ebgp-requires-policy + neighbor 172.31.12.9 remote-as 64511 + neighbor rrserver peer-group + neighbor rrserver remote-as 64500 + neighbor rrserver update-source lo + neighbor 192.0.2.3 peer-group rrserver + address-family ipv4 unicast + neighbor rrserver next-hop-self + neighbor rrserver addpath-tx-all-paths + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_backup_route/r5/isisd.conf b/tests/topotests/bgp_backup_route/r5/isisd.conf new file mode 100644 index 000000000000..06d01c18316c --- /dev/null +++ b/tests/topotests/bgp_backup_route/r5/isisd.conf @@ -0,0 +1,26 @@ +hostname r5 +interface lo + ip router isis 1 + isis passive +! +interface r5-eth1 + ip router isis 1 + isis network point-to-point +! +interface r5-eth2 + ip router isis 1 + isis network point-to-point +! +interface r5-eth3 + ip router isis 1 + isis network point-to-point +! +router isis 1 + net 49.0123.6452.0005.00 + is-type level-2-only + mpls-te on + segment-routing on + segment-routing global-block 16000 17000 + segment-routing node-msd 10 + segment-routing prefix 192.0.2.5/32 index 5 +! diff --git a/tests/topotests/bgp_backup_route/r5/zebra.conf b/tests/topotests/bgp_backup_route/r5/zebra.conf new file mode 100644 index 000000000000..6dce7564bc63 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r5/zebra.conf @@ -0,0 +1,20 @@ +log stdout +interface lo + ip address 192.0.2.5/32 +! +interface r5-eth0 + ip address 172.31.12.5/24 +! +interface r5-eth1 + ip address 172.31.4.5/24 +! +interface r5-eth2 + ip address 172.31.7.5/24 +! +interface r5-eth3 + ip address 172.31.16.5/24 +! +interface r5-eth4 + ip address 172.31.19.5/24 +! + diff --git a/tests/topotests/bgp_backup_route/r6/bgpd.conf b/tests/topotests/bgp_backup_route/r6/bgpd.conf new file mode 100644 index 000000000000..f9c69bc4b396 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r6/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 64500 + bgp router-id 192.0.2.6 + no bgp ebgp-requires-policy + neighbor 172.31.13.9 remote-as 64511 + neighbor rrserver peer-group + neighbor rrserver remote-as 64500 + neighbor rrserver update-source lo + neighbor 192.0.2.3 peer-group rrserver + address-family ipv4 unicast + neighbor rrserver next-hop-self + neighbor rrserver addpath-tx-all-paths + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_backup_route/r6/isisd.conf b/tests/topotests/bgp_backup_route/r6/isisd.conf new file mode 100644 index 000000000000..374cab63ac0f --- /dev/null +++ b/tests/topotests/bgp_backup_route/r6/isisd.conf @@ -0,0 +1,23 @@ +hostname r6 +interface lo + ip router isis 1 + isis passive + isis metric 20 +! +interface r6-eth1 + ip router isis 1 + isis network point-to-point +! +interface r6-eth2 + ip router isis 1 + isis network point-to-point +! +router isis 1 + net 49.0123.6452.0006.00 + is-type level-2-only + mpls-te on + segment-routing on + segment-routing global-block 16000 17000 + segment-routing node-msd 10 + segment-routing prefix 192.0.2.6/32 index 6 +! diff --git a/tests/topotests/bgp_backup_route/r6/zebra.conf b/tests/topotests/bgp_backup_route/r6/zebra.conf new file mode 100644 index 000000000000..cf5542026a14 --- /dev/null +++ b/tests/topotests/bgp_backup_route/r6/zebra.conf @@ -0,0 +1,14 @@ +log stdout +interface lo + ip address 192.0.2.6/32 +! +interface r6-eth0 + ip address 172.31.13.6/24 +! +interface r6-eth1 + ip address 172.31.5.6/24 +! +interface r6-eth2 + ip address 172.31.6.6/24 +! + diff --git a/tests/topotests/bgp_backup_route/test_bgp_backup_route.py b/tests/topotests/bgp_backup_route/test_bgp_backup_route.py new file mode 100644 index 000000000000..046d4a9af76f --- /dev/null +++ b/tests/topotests/bgp_backup_route/test_bgp_backup_route.py @@ -0,0 +1,707 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_set_aspath_replace.py +# +# Copyright 2023 6WIND S.A. +# + +""" + test_bgp_backup_route.py: Test the FRR BGP daemon with backup routes + + +--------+ +--------+ + | | | | + +---------------+ r10 +----------+ r11 +-------+ + | | + +-----+ | | + | +---+----+\ / +---+----+ | + | | \/ | | + | | /\ | | ++--------+ +---+----+ +---+----+/ \ +---+----+ | +| | | | | + +-----+ +-----+ | +| ce7 +----------+ r1 +----------+ r3 +----------+ r5 | | | +| | | | | rr + +-----+ | +--+-+--+ ++--------+ +---+----+ +--------+\ / +--------+ | | + | \/ | ce9 | + | /\ | | + | +--------+/ \ +--------+ +---+---+ + | | + +-----+ | | + +---------------+ r4 +----------+ r6 +------+ + | | | | + +--------+ +--------+ +""" + +import os +import sys +import json +from functools import partial +import pytest +import functools + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("ce7") + tgen.add_router("ce9") + # Create 7 PE routers. + tgen.add_router("r1") + tgen.add_router("r3") + tgen.add_router("r4") + tgen.add_router("r5") + tgen.add_router("r6") + tgen.add_router("r10") + tgen.add_router("r11") + + # switch + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["ce7"]) + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["ce9"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["ce9"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s10") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s11") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s12") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s13") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s14") + switch.add_link(tgen.gears["r10"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s15") + switch.add_link(tgen.gears["r10"]) + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s16") + switch.add_link(tgen.gears["r10"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s17") + switch.add_link(tgen.gears["r11"]) + switch.add_link(tgen.gears["r10"]) + + switch = tgen.add_switch("s18") + switch.add_link(tgen.gears["r11"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s19") + switch.add_link(tgen.gears["r11"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s20") + switch.add_link(tgen.gears["r11"]) + switch.add_link(tgen.gears["ce9"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add loop2 type dummy", + "ip link set dev loop2 up", + ] + + for name in ("ce7", "ce9"): + for cmd in cmds_list: + logger.info("input: " + cmd) + output = tgen.net[name].cmd(cmd) + logger.info("output: " + output) + + cmds_list = [ + "modprobe mpls_router", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + ] + + for name in ("r1", "r3", "r4", "r5", "r6", "r10", "r11"): + for cmd in cmds_list: + logger.info("input: " + cmd) + output = tgen.net[name].cmd(cmd) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + if rname in ("r1", "r3", "r4", "r5", "r6", "r10", "r11"): + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def bgp_check_path_selection_ecmp_backup(router, expected): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 192.0.2.9/32 json")) + return topotest.json_cmp(output, expected) + + +def bgp_check_path_selection_not_ecmp_backup(router, expected): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 192.0.2.9/32 json")) + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def bgp_ipv4_route_advertised_all_paths_to_ce7(): + """ + Check that all addpath routes are advertised to ce7 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "bestpath": {"overall": True}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["ce7"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "ce7, failed to check that 192.0.2.9/32 has 1 best path, and 2 other routes" + + +def bgp_check_route_primary_and_backup_advertised_to_ce7(): + """ + Check that only the primary and backup path routes are advertised to ce7 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("ce7, check that 192.0.2.9/32 has not 1 path and 2 other routes") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "bestpath": {"overall": True}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_not_ecmp_backup, tgen.gears["ce7"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "ce7, failed to check that 192.0.2.9/32 has not 1 best path, and 2 other routes" + + logger.info("ce7, check that 192.0.2.9/32 has 1 best path and 1 other routes") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "bestpath": {"overall": True}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["ce7"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "ce7, failed to check that 192.0.2.9/32 has 1 best path and 1 other route" + + +def test_ipv4_route_presence(): + """ + Assert that the 192.0.2.9/32 prefix is present + Check the presence of backup and primary routes when addpath-backup is configured. + The IGP is modified: + - by default, IGP metric for r6 is longer to reach than for r11 and r5 + - IGP metric for r6 is equal to the IGP metric for r11 and r5 + - IGP metric for r6 is equal to the IGP metric for r11 and r5 + - IGP metric for r5 and r11 is longer than for r6 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Check that 192.0.2.9/32 has 2 ECMP paths and 1 backup path") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64511"}, + "multipath": True, + "bestpath": { + "overall": True, + }, + "originatorId": "192.0.2.5", + "nexthops": [{"ip": "192.0.2.5", "metric": 30}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "multipath": True, + "originatorId": "192.0.2.11", + "nexthops": [{"ip": "192.0.2.11", "metric": 30}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "backup-bestpath": {"second": True}, + "originatorId": "192.0.2.6", + "nexthops": [{"ip": "192.0.2.6", "metric": 40}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["r1"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Failed to check that 192.0.2.9/32 has 2 ECMP paths and 1 backup path" + + logger.info("Changing IGP metric on r6 from 20 to 10") + tgen.gears["r6"].vtysh_cmd( + "configure terminal\ninterface lo\nisis metric 10\n", + isjson=False, + ) + + logger.info("Check that 192.0.2.9/32 has 3 ECMP paths") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64511"}, + "bestpath": {"overall": True}, + "multipath": True, + "originatorId": "192.0.2.5", + "nexthops": [{"ip": "192.0.2.5", "metric": 30}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "multipath": True, + "originatorId": "192.0.2.11", + "nexthops": [{"ip": "192.0.2.11", "metric": 30}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "multipath": True, + "originatorId": "192.0.2.6", + "nexthops": [{"ip": "192.0.2.6", "metric": 30}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["r1"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to check that 192.0.2.9/32 has 3 ECMP paths" + + logger.info("Changing IGP metric on r5 from 10 to 20") + tgen.gears["r5"].vtysh_cmd( + "configure terminal\ninterface lo\nisis metric 20\n", + isjson=False, + ) + logger.info("Changing IGP metric on r11 from 10 to 20") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\ninterface lo\nisis metric 20\n", + isjson=False, + ) + + logger.info("Check that 192.0.2.9/32 has 1 best path and 2 ECMP backup paths") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64511"}, + "backup-multipath": True, + "backup-bestpath": {"second": True}, + "originatorId": "192.0.2.5", + "nexthops": [{"ip": "192.0.2.5", "metric": 40}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "backup-multipath": True, + "originatorId": "192.0.2.11", + "nexthops": [{"ip": "192.0.2.11", "metric": 40}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "bestpath": {"overall": True}, + "originatorId": "192.0.2.6", + "nexthops": [{"ip": "192.0.2.6", "metric": 30}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["r1"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Failed to check that 192.0.2.9/32 has 1 best path and 2 ECMP backup paths" + + logger.info("Changing IGP metric on r11 from 20 to 30") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\ninterface lo\nisis metric 30\n", + isjson=False, + ) + logger.info( + "Check that 192.0.2.9/32 has 1 best path, 1 backup path, and an other route" + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64511"}, + "backup-bestpath": {"second": True}, + "originatorId": "192.0.2.5", + "nexthops": [{"ip": "192.0.2.5", "metric": 40}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "originatorId": "192.0.2.11", + "nexthops": [{"ip": "192.0.2.11", "metric": 50}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "bestpath": {"overall": True}, + "originatorId": "192.0.2.6", + "nexthops": [{"ip": "192.0.2.6", "metric": 30}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["r1"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=90, wait=0.5) + assert ( + result is None + ), "Failed to check that 192.0.2.9/32 has 1 best path, 1 backup path, and an other route" + bgp_ipv4_route_advertised_all_paths_to_ce7() + + +def test_configure_tx_backup_route_ce7(): + """ + Configure r1 to send only backup paths to ce7 + Check that only the primary and backup path routes are advertised to ce7 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring 'neighbor 172.31.10.7 addpath-tx-backup-paths") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 64500\naddress-family ipv4 unicast\nneighbor 172.31.10.7 addpath-tx-backup-paths\n", + isjson=False, + ) + bgp_check_route_primary_and_backup_advertised_to_ce7() + + +def test_ipv4_route_presence_when_unconfigured(): + """ + Check that there are no backup and backup-multipath routes when addpath-backup is unconfigured. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Unconfiguring 'addpath path-selection backup' on r1") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 64500\naddress-family ipv4 unicast\nno addpath path-selection backup\n", + isjson=False, + ) + + logger.info("Check that 192.0.2.9/32 has 1 best path") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64511"}, + "originatorId": "192.0.2.5", + "nexthops": [{"ip": "192.0.2.5", "metric": 40}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "originatorId": "192.0.2.11", + "nexthops": [{"ip": "192.0.2.11", "metric": 50}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "bestpath": {"overall": True}, + "originatorId": "192.0.2.6", + "nexthops": [{"ip": "192.0.2.6", "metric": 30}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["r1"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to check that 192.0.2.9/32 has 1 best path" + + logger.info("Check that 192.0.2.9/32 has no backup-bestpath") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64511"}, + "backup-bestpath": {"second": True}, + "originatorId": "192.0.2.5", + "nexthops": [{"ip": "192.0.2.5", "metric": 40}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "originatorId": "192.0.2.11", + "nexthops": [{"ip": "192.0.2.11", "metric": 50}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "bestpath": {"overall": True}, + "originatorId": "192.0.2.6", + "nexthops": [{"ip": "192.0.2.6", "metric": 30}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_not_ecmp_backup, tgen.gears["r1"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to check that 192.0.2.9/32 has no backup-bestpath" + + +def test_ipv4_route_advertised_primary_only_to_ce7(): + """ + Check that only the primary routes is advertised to ce7 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("ce7, check that 192.0.2.9/32 has not 1 path and not 1 backup routes") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "bestpath": {"overall": True}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_not_ecmp_backup, tgen.gears["ce7"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "ce7, failed to check that 192.0.2.9/32 has not 1 best path, and not 1 backup route" + + logger.info("ce7, check that 192.0.2.9/32 has 1 best path only") + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64500 64511"}, + "bestpath": {"overall": True}, + "nexthops": [{"ip": "172.31.10.1"}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["ce7"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "ce7, failed to check that 192.0.2.9/32 has 1 best path only" + + +def test_ipv4_route_presence_when_reconfigured(): + """ + Check that there are backup and backup-multipath routes when addpath-backup is configured. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring 'addpath path-selection backup' on r1") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 64500\naddress-family ipv4 unicast\naddpath path-selection backup\n", + isjson=False, + ) + + logger.info( + "Check that 192.0.2.9/32 has 1 best path, 1 backup path, and 1 other route" + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "64511"}, + "backup-bestpath": {"second": True}, + "originatorId": "192.0.2.5", + "nexthops": [{"ip": "192.0.2.5", "metric": 40}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "originatorId": "192.0.2.11", + "nexthops": [{"ip": "192.0.2.11", "metric": 50}], + }, + { + "valid": True, + "aspath": {"string": "64511"}, + "bestpath": {"overall": True}, + "originatorId": "192.0.2.6", + "nexthops": [{"ip": "192.0.2.6", "metric": 30}], + }, + ] + } + test_func = functools.partial( + bgp_check_path_selection_ecmp_backup, tgen.gears["r1"], expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Failed to check that 192.0.2.9/32 has 1 best path, 1 backup path, and 1 other route" + + bgp_check_route_primary_and_backup_advertised_to_ce7() + + +def test_ipv4_route_advertised_primary_and_backup_paths_to_ce7(): + """ + Configure r1 to send all paths to ce7 + Check that the three paths are advertised to ce7 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring 'neighbor 172.31.10.7 addpath-tx-all-paths") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 64500\naddress-family ipv4 unicast\nneighbor 172.31.10.7 addpath-tx-all-paths\n", + isjson=False, + ) + bgp_ipv4_route_advertised_all_paths_to_ce7() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))