From 000022d2056285b2826279641476062bad05d0e7 Mon Sep 17 00:00:00 2001 From: vivek Date: Sat, 28 Mar 2020 12:15:01 -0700 Subject: [PATCH] tests: Add tests for BGP link-bandwidth and weighted ECMP Implement tests to verify BGP link-bandwidth and weighted ECMP functionality. These tests validate one of the primary use cases for weighted ECMP (a.k.a. Unequal cost multipath) using BGP link-bandwidth: https://tools.ietf.org/html/draft-mohanty-bess-ebgp-dmz The included tests are: Test #1: Test BGP link-bandwidth advertisement based on number of multipaths Test #2: Test cumulative link-bandwidth propagation Test #3: Test weighted ECMP - multipath with next hop weights Test #4: Test weighted ECMP rebalancing upon change (link flap) Test #5: Test weighted ECMP for a second anycast IP Test #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP Test #7: Test different options for processing link-bandwidth on the receiver Signed-off-by: Vivek Venkatraman --- tests/topotests/bgp_link_bw_ip/__init__.py | 0 .../bgp_link_bw_ip/r1/bgp-route-1.json | 19 + .../bgp_link_bw_ip/r1/bgp-route-2.json | 32 ++ .../bgp_link_bw_ip/r1/bgp-route-3.json | 32 ++ .../bgp_link_bw_ip/r1/bgp-route-4.json | 32 ++ .../bgp_link_bw_ip/r1/bgp-route-5.json | 29 + tests/topotests/bgp_link_bw_ip/r1/bgpd.conf | 8 + .../bgp_link_bw_ip/r1/ip-route-1.json | 20 + .../bgp_link_bw_ip/r1/ip-route-2.json | 20 + .../bgp_link_bw_ip/r1/ip-route-3.json | 20 + .../bgp_link_bw_ip/r1/ip-route-4.json | 20 + .../bgp_link_bw_ip/r1/ip-route-5.json | 20 + .../bgp_link_bw_ip/r1/ip-route-6.json | 15 + .../bgp_link_bw_ip/r1/ip-route-7.json | 15 + .../bgp_link_bw_ip/r1/ip-route-8.json | 20 + .../bgp_link_bw_ip/r1/ip-route-9.json | 20 + .../topotests/bgp_link_bw_ip/r1/v4_route.json | 104 ++++ tests/topotests/bgp_link_bw_ip/r1/zebra.conf | 7 + tests/topotests/bgp_link_bw_ip/r10/bgpd.conf | 15 + tests/topotests/bgp_link_bw_ip/r10/zebra.conf | 6 + .../bgp_link_bw_ip/r2/bgp-route-1.json | 19 + .../bgp_link_bw_ip/r2/bgp-route-2.json | 19 + .../bgp_link_bw_ip/r2/bgp-route-3.json | 32 ++ tests/topotests/bgp_link_bw_ip/r2/bgpd.conf | 9 + .../bgp_link_bw_ip/r2/ip-route-1.json | 19 + .../bgp_link_bw_ip/r2/ip-route-2.json | 20 + .../bgp_link_bw_ip/r2/ip-route-3.json | 15 + tests/topotests/bgp_link_bw_ip/r2/zebra.conf | 10 + tests/topotests/bgp_link_bw_ip/r3/bgpd.conf | 8 + tests/topotests/bgp_link_bw_ip/r3/zebra.conf | 7 + .../bgp_link_bw_ip/r4/bgp-route-1.json | 23 + tests/topotests/bgp_link_bw_ip/r4/bgpd.conf | 28 + .../bgp_link_bw_ip/r4/ip-route-1.json | 21 + tests/topotests/bgp_link_bw_ip/r4/zebra.conf | 10 + tests/topotests/bgp_link_bw_ip/r5/bgpd.conf | 20 + tests/topotests/bgp_link_bw_ip/r5/zebra.conf | 7 + tests/topotests/bgp_link_bw_ip/r6/bgpd.conf | 20 + tests/topotests/bgp_link_bw_ip/r6/zebra.conf | 7 + tests/topotests/bgp_link_bw_ip/r7/bgpd.conf | 15 + tests/topotests/bgp_link_bw_ip/r7/zebra.conf | 6 + tests/topotests/bgp_link_bw_ip/r8/bgpd.conf | 15 + tests/topotests/bgp_link_bw_ip/r8/zebra.conf | 6 + tests/topotests/bgp_link_bw_ip/r9/bgpd.conf | 15 + tests/topotests/bgp_link_bw_ip/r9/zebra.conf | 6 + .../bgp_link_bw_ip/test_bgp_linkbw_ip.py | 515 ++++++++++++++++++ 45 files changed, 1326 insertions(+) create mode 100755 tests/topotests/bgp_link_bw_ip/__init__.py create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/v4_route.json create mode 100644 tests/topotests/bgp_link_bw_ip/r1/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r10/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r10/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json create mode 100644 tests/topotests/bgp_link_bw_ip/r2/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r3/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r3/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r4/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json create mode 100644 tests/topotests/bgp_link_bw_ip/r4/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r5/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r5/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r6/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r6/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r7/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r7/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r8/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r8/zebra.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r9/bgpd.conf create mode 100644 tests/topotests/bgp_link_bw_ip/r9/zebra.conf create mode 100755 tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py diff --git a/tests/topotests/bgp_link_bw_ip/__init__.py b/tests/topotests/bgp_link_bw_ip/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json new file mode 100644 index 000000000000..3e3c35ee08e4 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json new file mode 100644 index 000000000000..f07e89b4951d --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:375000 (3.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json new file mode 100644 index 000000000000..3501d12e70a7 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json new file mode 100644 index 000000000000..b1ed004490a0 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.11\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json new file mode 100644 index 000000000000..89469b8ace7c --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json @@ -0,0 +1,29 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:375000 (3.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf new file mode 100644 index 000000000000..595e244a21e6 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf @@ -0,0 +1,8 @@ +hostname r1 +! +router bgp 65101 + bgp router-id 11.1.1.1 + bgp bestpath as-path multipath-relax + neighbor 11.1.1.2 remote-as external + neighbor 11.1.1.6 remote-as external +! diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json new file mode 100644 index 000000000000..3c02e2675d12 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":25 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":75 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json new file mode 100644 index 000000000000..3c2d42caaca3 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json new file mode 100644 index 000000000000..3d80018ceab5 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json new file mode 100644 index 000000000000..6b757ef9ed91 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json new file mode 100644 index 000000000000..641ecabf47fd --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json new file mode 100644 index 000000000000..6ed3f8ef556e --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json @@ -0,0 +1,15 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json new file mode 100644 index 000000000000..95531d99be93 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json @@ -0,0 +1,15 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json new file mode 100644 index 000000000000..beac5013601e --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json new file mode 100644 index 000000000000..eb27ce2633b0 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/v4_route.json b/tests/topotests/bgp_link_bw_ip/r1/v4_route.json new file mode 100644 index 000000000000..d40a06d87224 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/v4_route.json @@ -0,0 +1,104 @@ +{ + "10.0.1.1\/32":[ + { + "prefix":"10.0.1.1\/32", + "protocol":"ospf", + "distance":110, + "metric":10, + "table":254, + "internalStatus":0, + "internalFlags":0, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":9, + "ip":"0.0.0.0", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "onLink":true + } + ] + }, + { + "prefix":"10.0.1.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.3.4\/32":[ + { + "prefix":"10.0.3.4\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "10.0.20.1\/32":[ + { + "prefix":"10.0.20.1\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":11, + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true, + "onLink":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/zebra.conf b/tests/topotests/bgp_link_bw_ip/r1/zebra.conf new file mode 100644 index 000000000000..0fc81f9bac85 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 11.1.1.1/30 +! +interface r1-eth1 + ip address 11.1.1.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf new file mode 100644 index 000000000000..88a7bdce2285 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf @@ -0,0 +1,15 @@ +hostname r10 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65354 + bgp router-id 11.1.6.2 + neighbor 11.1.6.1 remote-as external + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r10/zebra.conf b/tests/topotests/bgp_link_bw_ip/r10/zebra.conf new file mode 100644 index 000000000000..1a24fdaea7ca --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r10/zebra.conf @@ -0,0 +1,6 @@ +interface r10-eth0 + ip address 11.1.6.2/30 +! +interface r10-eth1 + ip address 50.1.1.10/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json new file mode 100644 index 000000000000..3c38689a3720 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json new file mode 100644 index 000000000000..1895cd822e79 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json new file mode 100644 index 000000000000..dfc4171bad7a --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65302:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf new file mode 100644 index 000000000000..2b6e9aeb6f50 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf @@ -0,0 +1,9 @@ +hostname r2 +! +router bgp 65201 + bgp router-id 11.1.2.1 + bgp bestpath as-path multipath-relax + neighbor 11.1.1.1 remote-as external + neighbor 11.1.2.2 remote-as external + neighbor 11.1.2.6 remote-as external +! diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json new file mode 100644 index 000000000000..131100a684cd --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json @@ -0,0 +1,19 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "protocol":"bgp", + "selected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.2", + "interfaceName":"r2-eth1", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json new file mode 100644 index 000000000000..7e2fa6be2504 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.2.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json new file mode 100644 index 000000000000..d0509bbd29fb --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json @@ -0,0 +1,15 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.2", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/zebra.conf b/tests/topotests/bgp_link_bw_ip/r2/zebra.conf new file mode 100644 index 000000000000..23573a108d4f --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/zebra.conf @@ -0,0 +1,10 @@ +! +interface r2-eth0 + ip address 11.1.1.2/30 +! +interface r2-eth1 + ip address 11.1.2.1/30 +! +interface r2-eth2 + ip address 11.1.2.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf new file mode 100644 index 000000000000..8b7c0c179293 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf @@ -0,0 +1,8 @@ +hostname r3 +! +router bgp 65202 + bgp router-id 11.1.3.1 + bgp bestpath as-path multipath-relax + neighbor 11.1.1.5 remote-as external + neighbor 11.1.3.2 remote-as external +! diff --git a/tests/topotests/bgp_link_bw_ip/r3/zebra.conf b/tests/topotests/bgp_link_bw_ip/r3/zebra.conf new file mode 100644 index 000000000000..d6676698212a --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r3/zebra.conf @@ -0,0 +1,7 @@ +! +interface r3-eth0 + ip address 11.1.1.6/30 +! +interface r3-eth1 + ip address 11.1.3.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json new file mode 100644 index 000000000000..87d1ae0b4481 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json @@ -0,0 +1,23 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.4.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.4.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf new file mode 100644 index 000000000000..fa1f37843fc6 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf @@ -0,0 +1,28 @@ +! +log file bgpd.log +! +debug bgp updates +debug bgp zebra +debug bgp bestpath 198.10.1.1/32 +! +hostname r4 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65301 + bgp router-id 11.1.4.1 + bgp bestpath as-path multipath-relax + neighbor 11.1.2.1 remote-as external + neighbor 11.1.4.2 remote-as external + neighbor 11.1.4.6 remote-as external + ! + address-family ipv4 unicast + neighbor 11.1.2.1 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json new file mode 100644 index 000000000000..a9ccf07c82ab --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json @@ -0,0 +1,21 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "protocol":"bgp", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.4.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.4.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r4/zebra.conf b/tests/topotests/bgp_link_bw_ip/r4/zebra.conf new file mode 100644 index 000000000000..ef61f7eb1b69 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/zebra.conf @@ -0,0 +1,10 @@ +! +interface r4-eth0 + ip address 11.1.2.2/30 +! +interface r4-eth1 + ip address 11.1.4.1/30 +! +interface r4-eth2 + ip address 11.1.4.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf new file mode 100644 index 000000000000..8614f3e178ac --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf @@ -0,0 +1,20 @@ +hostname r5 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65302 + bgp router-id 11.1.5.1 + bgp bestpath as-path multipath-relax + neighbor 11.1.2.5 remote-as external + neighbor 11.1.5.2 remote-as external + ! + address-family ipv4 unicast + neighbor 11.1.2.5 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r5/zebra.conf b/tests/topotests/bgp_link_bw_ip/r5/zebra.conf new file mode 100644 index 000000000000..66c65964e264 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r5/zebra.conf @@ -0,0 +1,7 @@ +! +interface r5-eth0 + ip address 11.1.2.6/30 +! +interface r5-eth1 + ip address 11.1.5.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf new file mode 100644 index 000000000000..3e5c6df6e1b6 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf @@ -0,0 +1,20 @@ +hostname r6 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65303 + bgp router-id 11.1.6.1 + bgp bestpath as-path multipath-relax + neighbor 11.1.3.1 remote-as external + neighbor 11.1.6.2 remote-as external + ! + address-family ipv4 unicast + neighbor 11.1.3.1 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r6/zebra.conf b/tests/topotests/bgp_link_bw_ip/r6/zebra.conf new file mode 100644 index 000000000000..66ff563269d3 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r6/zebra.conf @@ -0,0 +1,7 @@ +! +interface r6-eth0 + ip address 11.1.3.2/30 +! +interface r6-eth1 + ip address 11.1.6.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf new file mode 100644 index 000000000000..7862023f55f8 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf @@ -0,0 +1,15 @@ +hostname r7 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65351 + bgp router-id 11.1.4.2 + neighbor 11.1.4.1 remote-as external + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r7/zebra.conf b/tests/topotests/bgp_link_bw_ip/r7/zebra.conf new file mode 100644 index 000000000000..38e36cac308f --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r7/zebra.conf @@ -0,0 +1,6 @@ +interface r7-eth0 + ip address 11.1.4.2/30 +! +interface r7-eth1 + ip address 50.1.1.7/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf new file mode 100644 index 000000000000..02110d917585 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf @@ -0,0 +1,15 @@ +hostname r8 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65352 + bgp router-id 11.1.4.6 + neighbor 11.1.4.5 remote-as external + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r8/zebra.conf b/tests/topotests/bgp_link_bw_ip/r8/zebra.conf new file mode 100644 index 000000000000..1369e19c0665 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r8/zebra.conf @@ -0,0 +1,6 @@ +interface r8-eth0 + ip address 11.1.4.6/30 +! +interface r8-eth1 + ip address 50.1.1.8/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf new file mode 100644 index 000000000000..d64663fa16f2 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf @@ -0,0 +1,15 @@ +hostname r9 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65353 + bgp router-id 11.1.5.2 + neighbor 11.1.5.1 remote-as external + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r9/zebra.conf b/tests/topotests/bgp_link_bw_ip/r9/zebra.conf new file mode 100644 index 000000000000..c73caf3bfc8f --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r9/zebra.conf @@ -0,0 +1,6 @@ +interface r9-eth0 + ip address 11.1.5.2/30 +! +interface r9-eth1 + ip address 50.1.1.9/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py new file mode 100755 index 000000000000..86eb2969ce32 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py @@ -0,0 +1,515 @@ +#!/usr/bin/env python + +# +# test_bgp_linkbw_ip.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc +# Vivek Venkatraman +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_linkbw_ip.py: Test weighted ECMP using BGP link-bandwidth +""" + +import os +import re +import sys +from functools import partial +import pytest +import json + +# 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. +from mininet.topo import Topo + +""" +This topology is for validating one of the primary use cases for +weighted ECMP (a.k.a. Unequal cost multipath) using BGP link-bandwidth: +https://tools.ietf.org/html/draft-mohanty-bess-ebgp-dmz + +The topology consists of two PODs. Pod-1 consists of a spine switch +and two leaf switches, with two servers attached to the first leaf and +one to the second leaf. Pod-2 consists of one spine and one leaf, with +one server connected to the leaf. The PODs are connected by a super-spine +switch. + +Note that the use of the term "switch" above is in keeping with common +data-center terminology. These devices are all regular routers; for +this scenario, the servers are also routers as they have to announce +anycast IP (VIP) addresses via BGP. +""" + +class BgpLinkBwTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 10 routers - 1 super-spine, 2 spines, 3 leafs + # and 4 servers + routers = {} + for i in range(1, 11): + routers[i] = tgen.add_router('r{}'.format(i)) + + # Create 13 "switches" - to interconnect the above routers + switches = {} + for i in range(1, 14): + switches[i] = tgen.add_switch('s{}'.format(i)) + + # Interconnect R1 (super-spine) to R2 and R3 (the two spines) + switches[1].add_link(tgen.gears['r1']) + switches[1].add_link(tgen.gears['r2']) + switches[2].add_link(tgen.gears['r1']) + switches[2].add_link(tgen.gears['r3']) + + # Interconnect R2 (spine in pod-1) to R4 and R5 (the associated + # leaf switches) + switches[3].add_link(tgen.gears['r2']) + switches[3].add_link(tgen.gears['r4']) + switches[4].add_link(tgen.gears['r2']) + switches[4].add_link(tgen.gears['r5']) + + # Interconnect R3 (spine in pod-2) to R6 (associated leaf) + switches[5].add_link(tgen.gears['r3']) + switches[5].add_link(tgen.gears['r6']) + + # Interconnect leaf switches to servers + switches[6].add_link(tgen.gears['r4']) + switches[6].add_link(tgen.gears['r7']) + switches[7].add_link(tgen.gears['r4']) + switches[7].add_link(tgen.gears['r8']) + switches[8].add_link(tgen.gears['r5']) + switches[8].add_link(tgen.gears['r9']) + switches[9].add_link(tgen.gears['r6']) + switches[9].add_link(tgen.gears['r10']) + + # Create empty networks for the servers + switches[10].add_link(tgen.gears['r7']) + switches[11].add_link(tgen.gears['r8']) + switches[12].add_link(tgen.gears['r9']) + switches[13].add_link(tgen.gears['r10']) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BgpLinkBwTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + 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)) + ) + + # Initialize all routers. + tgen.start_router() + + #tgen.mininet_cli() + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_linkbw_adv(): + "Test #1: Test BGP link-bandwidth advertisement based on number of multipaths" + logger.info('\nTest #1: Test BGP link-bandwidth advertisement based on number of multipaths') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + + # Configure anycast IP on server r7 + logger.info('Configure anycast IP on server r7') + + tgen.net['r7'].cmd('ip addr add 198.10.1.1/32 dev r7-eth1') + + # Check on spine router r2 for link-bw advertisement by leaf router r4 + logger.info('Check on spine router r2 for link-bw advertisement by leaf router r4') + + json_file = '{}/r2/bgp-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Check on spine router r2 that default weight is used as there is no multipath + logger.info('Check on spine router r2 that default weight is used as there is no multipath') + + json_file = '{}/r2/ip-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Check on super-spine router r1 that link-bw has been propagated by spine router r2 + logger.info('Check on super-spine router r1 that link-bw has been propagated by spine router r2') + + json_file = '{}/r1/bgp-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_bgp_cumul_linkbw(): + "Test #2: Test cumulative link-bandwidth propagation" + logger.info('\nTest #2: Test cumulative link-bandwidth propagation') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + r4 = tgen.gears['r4'] + + # Configure anycast IP on additional server r8 + logger.info('Configure anycast IP on server r8') + + tgen.net['r8'].cmd('ip addr add 198.10.1.1/32 dev r8-eth1') + + # Check multipath on leaf router r4 + logger.info('Check multipath on leaf router r4') + + json_file = '{}/r4/bgp-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r4, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on leaf router r4' + assert result is None, assertmsg + + # Check regular ECMP is in effect on leaf router r4 + logger.info('Check regular ECMP is in effect on leaf router r4') + + json_file = '{}/r4/ip-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r4, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on leaf router r4' + assert result is None, assertmsg + + # Check on spine router r2 that leaf has propagated the cumulative link-bw based on num-multipaths + logger.info('Check on spine router r2 that leaf has propagated the cumulative link-bw based on num-multipaths') + + json_file = '{}/r2/bgp-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + +def test_weighted_ecmp(): + "Test #3: Test weighted ECMP - multipath with next hop weights" + logger.info('\nTest #3: Test weighted ECMP - multipath with next hop weights') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + + # Configure anycast IP on additional server r9 + logger.info('Configure anycast IP on server r9') + + tgen.net['r9'].cmd('ip addr add 198.10.1.1/32 dev r9-eth1') + + # Check multipath on spine router r2 + logger.info('Check multipath on spine router r2') + json_file = '{}/r2/bgp-route-3.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Check weighted ECMP is in effect on the spine router r2 + logger.info('Check weighted ECMP is in effect on the spine router r2') + + json_file = '{}/r2/ip-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Configure anycast IP on additional server r10 + logger.info('Configure anycast IP on server r10') + + tgen.net['r10'].cmd('ip addr add 198.10.1.1/32 dev r10-eth1') + + # Check multipath on super-spine router r1 + logger.info('Check multipath on super-spine router r1') + json_file = '{}/r1/bgp-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + # Check weighted ECMP is in effect on the super-spine router r1 + logger.info('Check weighted ECMP is in effect on the super-spine router r1') + json_file = '{}/r1/ip-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_weighted_ecmp_link_flap(): + "Test #4: Test weighted ECMP rebalancing upon change (link flap)" + logger.info('\nTest #4: Test weighted ECMP rebalancing upon change (link flap)') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + + # Bring down link on server r9 + logger.info('Bring down link on server r9') + + tgen.net['r9'].cmd('ip link set dev r9-eth1 down') + + # Check spine router r2 has only one path + logger.info('Check spine router r2 has only one path') + + json_file = '{}/r2/ip-route-3.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1 + logger.info('Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1') + + json_file = '{}/r1/bgp-route-3.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + # Bring up link on server r9 + logger.info('Bring up link on server r9') + + tgen.net['r9'].cmd('ip link set dev r9-eth1 up') + + # Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1 + logger.info('Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1') + + json_file = '{}/r1/bgp-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_weighted_ecmp_second_anycast_ip(): + "Test #5: Test weighted ECMP for a second anycast IP" + logger.info('\nTest #5: Test weighted ECMP for a second anycast IP') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + + # Configure anycast IP on additional server r7, r9 and r10 + logger.info('Configure anycast IP on server r7, r9 and r10') + + tgen.net['r7'].cmd('ip addr add 198.10.1.11/32 dev r7-eth1') + tgen.net['r9'].cmd('ip addr add 198.10.1.11/32 dev r9-eth1') + tgen.net['r10'].cmd('ip addr add 198.10.1.11/32 dev r10-eth1') + + # Check link-bandwidth and weighted ECMP on super-spine router r1 + logger.info('Check link-bandwidth and weighted ECMP on super-spine router r1') + + json_file = '{}/r1/bgp-route-4.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-3.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_paths_with_and_without_linkbw(): + "Test #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP" + logger.info('\nTest #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + + # Configure leaf router r6 to not advertise any link-bandwidth + logger.info('Configure leaf router r6 to not advertise any link-bandwidth') + + tgen.net['r6'].cmd('vtysh -c \"conf t\" -c \"router bgp 65303\" -c \"address-family ipv4 unicast\" -c \"no neighbor 11.1.3.1 route-map anycast_ip out\"') + + # Check link-bandwidth change on super-spine router r1 + logger.info('Check link-bandwidth change on super-spine router r1') + + json_file = '{}/r1/bgp-route-5.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + # Check super-spine router r1 resorts to regular ECMP + logger.info('Check super-spine router r1 resorts to regular ECMP') + + json_file = '{}/r1/ip-route-4.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-5.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_linkbw_handling_options(): + "Test #7: Test different options for processing link-bandwidth on the receiver" + logger.info('\nTest #7: Test different options for processing link-bandwidth on the receiver') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + + # Configure super-spine r1 to skip multipaths without link-bandwidth + logger.info('Configure super-spine r1 to skip multipaths without link-bandwidth') + + tgen.net['r1'].cmd('vtysh -c \"conf t\" -c \"router bgp 65101\" -c \"bgp bestpath bandwidth skip-missing\"') + + # Check super-spine router r1 resorts to only one path as other path is skipped + logger.info('Check super-spine router r1 resorts to only one path as other path is skipped') + + json_file = '{}/r1/ip-route-6.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-7.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + # Configure super-spine r1 to use default-weight for multipaths without link-bandwidth + logger.info('Configure super-spine r1 to use default-weight for multipaths without link-bandwidth') + + tgen.net['r1'].cmd('vtysh -c \"conf t\" -c \"router bgp 65101\" -c \"bgp bestpath bandwidth default-weight-for-missing\"') + + # Check super-spine router r1 uses ECMP with weight 1 for path without link-bandwidth + logger.info('Check super-spine router r1 uses ECMP with weight 1 for path without link-bandwidth') + + json_file = '{}/r1/ip-route-8.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-9.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))