]> git.puffer.fish Git - mirror/frr.git/commitdiff
tests: Add tests for BGP link-bandwidth and weighted ECMP
authorvivek <vivek@cumulusnetworks.com>
Sat, 28 Mar 2020 19:15:01 +0000 (12:15 -0700)
committervivek <vivek@cumulusnetworks.com>
Tue, 31 Mar 2020 03:12:32 +0000 (20:12 -0700)
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 <vivek@cumulusnetworks.com>
45 files changed:
tests/topotests/bgp_link_bw_ip/__init__.py [new file with mode: 0755]
tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/v4_route.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r10/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r10/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r3/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r3/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r4/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r4/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r5/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r5/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r6/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r6/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r7/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r7/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r8/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r8/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r9/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/r9/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py [new file with mode: 0755]

diff --git a/tests/topotests/bgp_link_bw_ip/__init__.py b/tests/topotests/bgp_link_bw_ip/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
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 (file)
index 0000000..3e3c35e
--- /dev/null
@@ -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 (file)
index 0000000..f07e89b
--- /dev/null
@@ -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 (file)
index 0000000..3501d12
--- /dev/null
@@ -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 (file)
index 0000000..b1ed004
--- /dev/null
@@ -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 (file)
index 0000000..89469b8
--- /dev/null
@@ -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 (file)
index 0000000..595e244
--- /dev/null
@@ -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 (file)
index 0000000..3c02e26
--- /dev/null
@@ -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 (file)
index 0000000..3c2d42c
--- /dev/null
@@ -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 (file)
index 0000000..3d80018
--- /dev/null
@@ -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 (file)
index 0000000..6b757ef
--- /dev/null
@@ -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 (file)
index 0000000..641ecab
--- /dev/null
@@ -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 (file)
index 0000000..6ed3f8e
--- /dev/null
@@ -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 (file)
index 0000000..95531d9
--- /dev/null
@@ -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 (file)
index 0000000..beac501
--- /dev/null
@@ -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 (file)
index 0000000..eb27ce2
--- /dev/null
@@ -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 (file)
index 0000000..d40a06d
--- /dev/null
@@ -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 (file)
index 0000000..0fc81f9
--- /dev/null
@@ -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 (file)
index 0000000..88a7bdc
--- /dev/null
@@ -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 (file)
index 0000000..1a24fda
--- /dev/null
@@ -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 (file)
index 0000000..3c38689
--- /dev/null
@@ -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 (file)
index 0000000..1895cd8
--- /dev/null
@@ -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 (file)
index 0000000..dfc4171
--- /dev/null
@@ -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 (file)
index 0000000..2b6e9ae
--- /dev/null
@@ -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 (file)
index 0000000..131100a
--- /dev/null
@@ -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 (file)
index 0000000..7e2fa6b
--- /dev/null
@@ -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 (file)
index 0000000..d0509bb
--- /dev/null
@@ -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 (file)
index 0000000..23573a1
--- /dev/null
@@ -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 (file)
index 0000000..8b7c0c1
--- /dev/null
@@ -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 (file)
index 0000000..d667669
--- /dev/null
@@ -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 (file)
index 0000000..87d1ae0
--- /dev/null
@@ -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 (file)
index 0000000..fa1f378
--- /dev/null
@@ -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 (file)
index 0000000..a9ccf07
--- /dev/null
@@ -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 (file)
index 0000000..ef61f7e
--- /dev/null
@@ -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 (file)
index 0000000..8614f3e
--- /dev/null
@@ -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 (file)
index 0000000..66c6596
--- /dev/null
@@ -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 (file)
index 0000000..3e5c6df
--- /dev/null
@@ -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 (file)
index 0000000..66ff563
--- /dev/null
@@ -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 (file)
index 0000000..7862023
--- /dev/null
@@ -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 (file)
index 0000000..38e36ca
--- /dev/null
@@ -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 (file)
index 0000000..02110d9
--- /dev/null
@@ -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 (file)
index 0000000..1369e19
--- /dev/null
@@ -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 (file)
index 0000000..d64663f
--- /dev/null
@@ -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 (file)
index 0000000..c73caf3
--- /dev/null
@@ -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 (executable)
index 0000000..86eb296
--- /dev/null
@@ -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))