summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/bgpd/test_aspath.c125
-rw-r--r--tests/isisd/test_isis_lspdb.c1
-rw-r--r--tests/lib/test_checksum.c3
-rw-r--r--tests/lib/test_ttable.c3
-rw-r--r--tests/topotests/Dockerfile6
-rw-r--r--tests/topotests/all-protocol-startup/r1/show_route_map.ref15
-rw-r--r--tests/topotests/bfd-isis-topo1/rt1/bfdd.conf2
-rw-r--r--tests/topotests/bfd-isis-topo1/rt2/bfdd.conf1
-rw-r--r--tests/topotests/bfd-isis-topo1/rt3/bfdd.conf1
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json4
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bfdd.conf9
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bgpd.conf2
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/isisd.conf1
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json4
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/bgpd.conf2
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/isisd.conf1
-rwxr-xr-x[-rw-r--r--]tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py0
-rw-r--r--tests/topotests/bfd-topo3/__init__.py0
-rw-r--r--tests/topotests/bfd-topo3/r1/bfd-peers.json68
-rw-r--r--tests/topotests/bfd-topo3/r1/bfdd.conf17
-rw-r--r--tests/topotests/bfd-topo3/r1/bgpd.conf20
-rw-r--r--tests/topotests/bfd-topo3/r1/zebra.conf10
-rw-r--r--tests/topotests/bfd-topo3/r2/bfd-peers.json46
-rw-r--r--tests/topotests/bfd-topo3/r2/bfdd.conf15
-rw-r--r--tests/topotests/bfd-topo3/r2/bgpd.conf15
-rw-r--r--tests/topotests/bfd-topo3/r2/zebra.conf14
-rw-r--r--tests/topotests/bfd-topo3/r3/bfd-peers.json68
-rw-r--r--tests/topotests/bfd-topo3/r3/bfdd.conf11
-rw-r--r--tests/topotests/bfd-topo3/r3/bgpd.conf19
-rw-r--r--tests/topotests/bfd-topo3/r3/zebra.conf14
-rw-r--r--tests/topotests/bfd-topo3/r4/bfd-peers.json46
-rw-r--r--tests/topotests/bfd-topo3/r4/bfdd.conf16
-rw-r--r--tests/topotests/bfd-topo3/r4/bgpd.conf16
-rw-r--r--tests/topotests/bfd-topo3/r4/zebra.conf10
-rw-r--r--tests/topotests/bfd-topo3/test_bfd_topo3.dot73
-rw-r--r--tests/topotests/bfd-topo3/test_bfd_topo3.jpgbin0 -> 34705 bytes
-rw-r--r--tests/topotests/bfd-topo3/test_bfd_topo3.py191
-rw-r--r--tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdfbin0 -> 90963 bytes
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd11/evpn.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd11/pim.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd11/zebra.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd12/evpn.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd12/pim.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd12/zebra.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd21/evpn.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd21/pim.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd21/zebra.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd22/evpn.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd22/pim.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd22/zebra.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/spine1/evpn.conf17
-rw-r--r--tests/topotests/bgp-evpn-mh/spine1/pim.conf18
-rw-r--r--tests/topotests/bgp-evpn-mh/spine1/zebra.conf15
-rw-r--r--tests/topotests/bgp-evpn-mh/spine2/evpn.conf17
-rw-r--r--tests/topotests/bgp-evpn-mh/spine2/pim.conf18
-rw-r--r--tests/topotests/bgp-evpn-mh/spine2/zebra.conf15
-rwxr-xr-xtests/topotests/bgp-evpn-mh/test_evpn_mh.py651
-rw-r--r--tests/topotests/bgp-evpn-mh/torm11/evpn.conf21
-rw-r--r--tests/topotests/bgp-evpn-mh/torm11/pim.conf13
-rw-r--r--tests/topotests/bgp-evpn-mh/torm11/zebra.conf23
-rw-r--r--tests/topotests/bgp-evpn-mh/torm12/evpn.conf21
-rw-r--r--tests/topotests/bgp-evpn-mh/torm12/pim.conf13
-rw-r--r--tests/topotests/bgp-evpn-mh/torm12/zebra.conf23
-rw-r--r--tests/topotests/bgp-evpn-mh/torm21/evpn.conf21
-rw-r--r--tests/topotests/bgp-evpn-mh/torm21/pim.conf13
-rw-r--r--tests/topotests/bgp-evpn-mh/torm21/zebra.conf23
-rw-r--r--tests/topotests/bgp-evpn-mh/torm22/evpn.conf21
-rw-r--r--tests/topotests/bgp-evpn-mh/torm22/pim.conf13
-rw-r--r--tests/topotests/bgp-evpn-mh/torm22/zebra.conf23
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json3
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf2
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json3
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf2
-rwxr-xr-xtests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py119
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_prefix_sid/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf1
-rw-r--r--tests/topotests/evpn_type5_test_topo1/__init__.py0
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json887
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json887
-rwxr-xr-xtests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py1047
-rwxr-xr-xtests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py2117
-rw-r--r--tests/topotests/isis-sr-topo1/rt1/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt2/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt3/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt4/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt5/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt6/isisd.conf1
-rwxr-xr-xtests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py12
-rw-r--r--tests/topotests/lib/bgp.py724
-rw-r--r--tests/topotests/lib/common_config.py571
-rw-r--r--tests/topotests/lib/topogen.py4
-rw-r--r--tests/topotests/lib/topotest.py125
-rw-r--r--tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json18
-rw-r--r--tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json27
-rw-r--r--tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json15
-rw-r--r--tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json18
-rw-r--r--tests/topotests/pbr-topo1/r1/pbr-map.json26
-rw-r--r--tests/topotests/pbr-topo1/r1/pbrd.conf10
-rw-r--r--tests/topotests/pytest.ini2
-rwxr-xr-xtests/topotests/route-scale/test_route_scale.py156
-rw-r--r--tests/topotests/zebra_netlink/__init__.py0
-rw-r--r--tests/topotests/zebra_netlink/r1/sharpd.conf0
-rw-r--r--tests/topotests/zebra_netlink/r1/v4_route.json2802
-rw-r--r--tests/topotests/zebra_netlink/r1/zebra.conf2
-rwxr-xr-xtests/topotests/zebra_netlink/test_zebra_netlink.py131
127 files changed, 11251 insertions, 317 deletions
diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c
index b94355e8b8..439891b559 100644
--- a/tests/bgpd/test_aspath.c
+++ b/tests/bgpd/test_aspath.c
@@ -271,57 +271,9 @@ static struct test_segment {
0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
},
502,
- {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285",
-
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+ {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+
+ "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285",
250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466},
},
{
@@ -383,15 +335,13 @@ static struct test_segment {
{
/* 20 */
"reconcile_confed",
- "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665)"
- " set(23456,23456,23456), seq(23456,23456,23456)",
+ "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)",
{0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15, 0x4, 0x3,
0x01, 0xc8, 0x00, 0x7c, 0x03, 0x14, 0x2, 0x3, 0x19, 0x23,
0xe8, 0x10, 0x54, 0xa1, 0x1, 0x3, 0x5b, 0xa0, 0x5b, 0xa0,
0x5b, 0xa0, 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0},
40,
- {"(123 456 789) [124,456,788] 6435 59408 21665"
- " {23456} 23456 23456 23456",
+ {"(123 456 789) [124,456,788] 6435 59408 21665 {23456} 23456 23456 23456",
"6435 59408 21665 {23456} 23456 23456 23456", 7, 4,
NOT_ALL_PRIVATE, 23456, 1, 6435},
},
@@ -739,10 +689,8 @@ static struct tests {
/* 3 */
{&test_segments[4],
&test_segments[5],
- {"8467 59649 {4196,48658} {17322,30745} 6435 59408 21665"
- " {2457,4369,61697} 1842 41590 51793",
- "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665"
- " {2457,4369,61697} 1842 41590 51793",
+ {"8467 59649 {4196,48658} {17322,30745} 6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
11, 0, NOT_ALL_PRIVATE, 61697, 1, 8467}},
/* 4 */
{
@@ -777,59 +725,9 @@ static struct tests {
{
&test_segments[14],
&test_segments[11],
- {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 2 52737 4096 8722 4 8722",
-
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
- "8466 2 52737 4096 8722 4 8722",
+ {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 2 52737 4096 8722 4 8722",
+
+ "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 2 52737 4096 8722 4 8722",
257, 0, NOT_ALL_PRIVATE, 4096, 1000, 8466},
},
{NULL,
@@ -861,8 +759,7 @@ struct tests reconcile_tests[] = {
{
&test_segments[20],
&test_segments[19],
- {"(123 456 789) [124,456,788] 6435 59408 21665"
- " {2457,4369,61697} 1842 41591 51793",
+ {"(123 456 789) [124,456,788] 6435 59408 21665 {2457,4369,61697} 1842 41591 51793",
"6435 59408 21665 {2457,4369,61697} 1842 41591 51793", 7, 4,
NOT_ALL_PRIVATE, 51793, 1, 6435},
},
diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c
index f0baa482c7..6a571eaa84 100644
--- a/tests/isisd/test_isis_lspdb.c
+++ b/tests/isisd/test_isis_lspdb.c
@@ -82,6 +82,7 @@ static void test_lsp_build_list_nonzero_ht(void)
int main(int argc, char **argv)
{
+ struct isis *isis = NULL;
isis = calloc(sizeof(*isis), 1);
test_lsp_build_list_nonzero_ht();
return 0;
diff --git a/tests/lib/test_checksum.c b/tests/lib/test_checksum.c
index ddb76c8f9d..301078867a 100644
--- a/tests/lib/test_checksum.c
+++ b/tests/lib/test_checksum.c
@@ -489,8 +489,7 @@ int main(int argc, char **argv)
in_csum_res = in_cksum_optimized(buffer, exercise);
in_csum_rfc = in_cksum_rfc(buffer, exercise);
if (in_csum_res != in_csum || in_csum != in_csum_rfc)
- printf("verify: in_chksum failed in_csum:%x, in_csum_res:%x,"
- "in_csum_rfc %x, len:%d\n",
+ printf("verify: in_chksum failed in_csum:%x, in_csum_res:%x,in_csum_rfc %x, len:%d\n",
in_csum, in_csum_res, in_csum_rfc, exercise);
ospfd = ospfd_checksum(buffer, exercise + sizeof(uint16_t),
diff --git a/tests/lib/test_ttable.c b/tests/lib/test_ttable.c
index 674179b6ab..43b8adcb97 100644
--- a/tests/lib/test_ttable.c
+++ b/tests/lib/test_ttable.c
@@ -93,8 +93,7 @@ int main(int argc, char **argv)
/* add bigger row */
ttable_add_row(tt, "%s|%s||%s|%s",
- "nebula dusk session streets twilight "
- "pioneer beats yeah",
+ "nebula dusk session streets twilight pioneer beats yeah",
"prarie dog", "cornmeal", ":O -*_-*");
assert(tt->ncols == 5);
assert(tt->nrows == 2);
diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile
index cdd0ae2f6e..b7c6298228 100644
--- a/tests/topotests/Dockerfile
+++ b/tests/topotests/Dockerfile
@@ -19,6 +19,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \
libjson-c-dev \
libpcre3-dev \
libpython-dev \
+ libpython3-dev \
libreadline-dev \
libc-ares-dev \
libcap-dev \
@@ -26,7 +27,10 @@ RUN export DEBIAN_FRONTEND=noninteractive \
mininet \
pkg-config \
python-pip \
- python-sphinx \
+ python3 \
+ python3-dev \
+ python3-sphinx \
+ python3-pytest \
rsync \
strace \
tcpdump \
diff --git a/tests/topotests/all-protocol-startup/r1/show_route_map.ref b/tests/topotests/all-protocol-startup/r1/show_route_map.ref
index 25786081d1..612d0a729d 100644
--- a/tests/topotests/all-protocol-startup/r1/show_route_map.ref
+++ b/tests/topotests/all-protocol-startup/r1/show_route_map.ref
@@ -1,5 +1,5 @@
ZEBRA:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -8,7 +8,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
RIP:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -17,7 +17,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
RIPNG:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -26,7 +26,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
OSPF:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -35,7 +35,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
OSPF6:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -44,7 +44,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
BGP:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -52,7 +52,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Call clause:
Action:
Exit routemap
-route-map: bgp-map Invoked: 0 Optimization: enabled
+route-map: bgp-map Invoked: 0 Optimization: enabled Processed Change: false
permit, sequence 10 Invoked 0
Match clauses:
Set clauses:
@@ -70,4 +70,3 @@ route-map: bgp-map Invoked: 0 Optimization: enabled
Action:
Exit routemap
ISIS:
-SHARP:
diff --git a/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf
index 4793155939..57f9cd9e3d 100644
--- a/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf
+++ b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf
@@ -8,10 +8,12 @@ bfd
detect-multiplier 3
receive-interval 300
transmit-interval 300
+ no shutdown
!
peer 10.0.2.2 interface eth-rt3
detect-multiplier 3
receive-interval 300
transmit-interval 300
+ no shutdown
!
!
diff --git a/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf
index a49cd4fa6b..6b34e337d3 100644
--- a/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf
+++ b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf
@@ -8,5 +8,6 @@ bfd
detect-multiplier 3
receive-interval 300
transmit-interval 300
+ no shutdown
!
!
diff --git a/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf
index 600054a0bc..22937fe46f 100644
--- a/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf
+++ b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf
@@ -8,5 +8,6 @@ bfd
detect-multiplier 3
receive-interval 300
transmit-interval 300
+ no shutdown
!
!
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
index abca1ed131..d2d0c601c3 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
@@ -25,14 +25,14 @@
"local": "*",
"multihop": false,
"peer": "*",
- "receive-interval": 300,
+ "receive-interval": 250,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
"remote-id": "*",
"remote-receive-interval": 300,
"remote-transmit-interval": 300,
"status": "up",
- "transmit-interval": 300,
+ "transmit-interval": 250,
"uptime": "*",
"vrf": "default"
}
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
index 74dae5a60d..08eb0468d6 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
@@ -3,9 +3,8 @@ debug bfd network
debug bfd zebra
!
bfd
- ! profile is commented out on purpose.
- !profile fasttx
- ! receive-interval 250
- ! transmit-interval 250
- !!
+ profile fasttx
+ receive-interval 250
+ transmit-interval 250
+ !
!
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
index 9c56a349ed..c7b75d2fde 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
@@ -1,7 +1,7 @@
router bgp 100
bgp router-id 10.254.254.3
neighbor 172.16.1.2 remote-as 100
- neighbor 172.16.1.2 bfd profile fasttx
+ neighbor 172.16.1.2 bfd profile DOES_NOT_EXIST
address-family ipv4 unicast
redistribute connected
exit-address-family
diff --git a/tests/topotests/bfd-profiles-topo1/r3/isisd.conf b/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
index 5d774a356b..d27a783adf 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
@@ -8,6 +8,7 @@ interface r3-eth1
ipv6 router isis lan
isis circuit-type level-1
isis bfd
+ isis bfd profile fasttx
!
router isis lan
net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
diff --git a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
index c8bc4c20e9..2c2e136abf 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
@@ -11,8 +11,8 @@
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
"remote-id": "*",
- "remote-receive-interval": 300,
- "remote-transmit-interval": 300,
+ "remote-receive-interval": 250,
+ "remote-transmit-interval": 250,
"status": "up",
"transmit-interval": 300,
"uptime": "*",
diff --git a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
index 7c4b39b020..aff1016dee 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
@@ -5,7 +5,7 @@ router bgp 200
no bgp ebgp-requires-policy
neighbor 2001:db8:1::2 remote-as 100
neighbor 2001:db8:1::2 ebgp-multihop 2
- neighbor 2001:db8:1::2 bfd profile fasttx
+ neighbor 2001:db8:1::2 bfd profile DOES_NOT_EXIST
address-family ipv4 unicast
redistribute connected
exit-address-family
diff --git a/tests/topotests/bfd-profiles-topo1/r4/isisd.conf b/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
index 477740087d..01e197bed5 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
@@ -8,6 +8,7 @@ interface r4-eth0
ipv6 router isis lan
isis circuit-type level-1
isis bfd
+ isis bfd profile DOES_NOT_EXIST
!
router isis lan
net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
diff --git a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
index 02385b32e5..02385b32e5 100644..100755
--- a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
+++ b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
diff --git a/tests/topotests/bfd-topo3/__init__.py b/tests/topotests/bfd-topo3/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bfd-topo3/__init__.py
diff --git a/tests/topotests/bfd-topo3/r1/bfd-peers.json b/tests/topotests/bfd-topo3/r1/bfd-peers.json
new file mode 100644
index 0000000000..56205d538b
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r1/bfd-peers.json
@@ -0,0 +1,68 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "local": "2001:db8:1::1",
+ "minimum-ttl": 253,
+ "multihop": true,
+ "passive-mode": true,
+ "peer": "2001:db8:3::1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r1-eth0",
+ "local": "2001:db8:1::1",
+ "multihop": false,
+ "passive-mode": true,
+ "peer": "2001:db8:1::2",
+ "receive-interval": 600,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 600,
+ "remote-transmit-interval": 600,
+ "status": "up",
+ "transmit-interval": 600,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "local": "192.168.1.1",
+ "minimum-ttl": 254,
+ "multihop": true,
+ "passive-mode": true,
+ "peer": "192.168.2.1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd-topo3/r1/bfdd.conf b/tests/topotests/bfd-topo3/r1/bfdd.conf
new file mode 100644
index 0000000000..8e40b76d41
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r1/bfdd.conf
@@ -0,0 +1,17 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile fast-tx
+ receive-interval 600
+ transmit-interval 600
+ passive-mode
+ !
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ passive-mode
+ !
+!
diff --git a/tests/topotests/bfd-topo3/r1/bgpd.conf b/tests/topotests/bfd-topo3/r1/bgpd.conf
new file mode 100644
index 0000000000..a0281d50c6
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r1/bgpd.conf
@@ -0,0 +1,20 @@
+router bgp 100
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::2 remote-as internal
+ neighbor 2001:db8:1::2 bfd profile fast-tx
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 ebgp-multihop 2
+ neighbor 192.168.2.1 bfd profile slow-tx
+ neighbor 2001:db8:3::1 remote-as external
+ neighbor 2001:db8:3::1 ebgp-multihop 3
+ neighbor 2001:db8:3::1 bfd profile slow-tx
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::2 activate
+ neighbor 192.168.2.1 activate
+ neighbor 2001:db8:3::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-topo3/r1/zebra.conf b/tests/topotests/bfd-topo3/r1/zebra.conf
new file mode 100644
index 0000000000..64aee48436
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r1/zebra.conf
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+ ipv6 address 2001:db8:1::1/64
+!
diff --git a/tests/topotests/bfd-topo3/r2/bfd-peers.json b/tests/topotests/bfd-topo3/r2/bfd-peers.json
new file mode 100644
index 0000000000..cb8985b13e
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r2/bfd-peers.json
@@ -0,0 +1,46 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r2-eth0",
+ "local": "2001:db8:1::2",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:1::1",
+ "receive-interval": 600,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 600,
+ "remote-transmit-interval": 600,
+ "status": "up",
+ "transmit-interval": 600,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r2-eth1",
+ "local": "2001:db8:2::2",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:2::1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd-topo3/r2/bfdd.conf b/tests/topotests/bfd-topo3/r2/bfdd.conf
new file mode 100644
index 0000000000..2a92e463e0
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r2/bfdd.conf
@@ -0,0 +1,15 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile fast-tx
+ receive-interval 600
+ transmit-interval 600
+ !
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ !
+!
diff --git a/tests/topotests/bfd-topo3/r2/bgpd.conf b/tests/topotests/bfd-topo3/r2/bgpd.conf
new file mode 100644
index 0000000000..0e96033023
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r2/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 100
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::1 remote-as internal
+ neighbor 2001:db8:1::1 bfd profile fast-tx
+ neighbor 2001:db8:2::1 remote-as external
+ neighbor 2001:db8:2::1 bfd profile slow-tx
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::1 activate
+ neighbor 2001:db8:2::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-topo3/r2/zebra.conf b/tests/topotests/bfd-topo3/r2/zebra.conf
new file mode 100644
index 0000000000..c7e22d4804
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r2/zebra.conf
@@ -0,0 +1,14 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.2/32
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+ ipv6 address 2001:db8:1::2/64
+!
+interface r2-eth1
+ ip address 192.168.2.2/24
+ ipv6 address 2001:db8:2::2/64
+!
diff --git a/tests/topotests/bfd-topo3/r3/bfd-peers.json b/tests/topotests/bfd-topo3/r3/bfd-peers.json
new file mode 100644
index 0000000000..8be35fd084
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r3/bfd-peers.json
@@ -0,0 +1,68 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r3-eth1",
+ "local": "2001:db8:3::2",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:3::1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r3-eth0",
+ "local": "2001:db8:2::1",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:2::2",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "local": "192.168.2.1",
+ "minimum-ttl": 254,
+ "multihop": true,
+ "passive-mode": false,
+ "peer": "192.168.1.1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd-topo3/r3/bfdd.conf b/tests/topotests/bfd-topo3/r3/bfdd.conf
new file mode 100644
index 0000000000..f7972c6ce5
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r3/bfdd.conf
@@ -0,0 +1,11 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ !
+!
diff --git a/tests/topotests/bfd-topo3/r3/bgpd.conf b/tests/topotests/bfd-topo3/r3/bgpd.conf
new file mode 100644
index 0000000000..e14d2011a0
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r3/bgpd.conf
@@ -0,0 +1,19 @@
+router bgp 300
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 ebgp-multihop 2
+ neighbor 192.168.1.1 bfd profile slow-tx
+ neighbor 2001:db8:2::2 remote-as external
+ neighbor 2001:db8:2::2 bfd profile slow-tx
+ neighbor 2001:db8:3::1 remote-as external
+ neighbor 2001:db8:3::1 bfd profile slow-tx
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 192.168.1.1 activate
+ neighbor 2001:db8:2::2 activate
+ neighbor 2001:db8:3::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-topo3/r3/zebra.conf b/tests/topotests/bfd-topo3/r3/zebra.conf
new file mode 100644
index 0000000000..14248fb6f7
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r3/zebra.conf
@@ -0,0 +1,14 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.3/32
+!
+interface r3-eth0
+ ip address 192.168.2.1/24
+ ipv6 address 2001:db8:2::1/64
+!
+interface r3-eth1
+ ip address 192.168.3.2/24
+ ipv6 address 2001:db8:3::2/64
+!
diff --git a/tests/topotests/bfd-topo3/r4/bfd-peers.json b/tests/topotests/bfd-topo3/r4/bfd-peers.json
new file mode 100644
index 0000000000..e2e6722ef4
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r4/bfd-peers.json
@@ -0,0 +1,46 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "local": "2001:db8:3::1",
+ "minimum-ttl": 253,
+ "multihop": true,
+ "passive-mode": false,
+ "peer": "2001:db8:1::1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r4-eth0",
+ "local": "2001:db8:3::1",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:3::2",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd-topo3/r4/bfdd.conf b/tests/topotests/bfd-topo3/r4/bfdd.conf
new file mode 100644
index 0000000000..f44abc0b8a
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r4/bfdd.conf
@@ -0,0 +1,16 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ !
+ profile slow-tx-mh
+ receive-interval 2000
+ transmit-interval 2000
+ minimum-ttl 250
+ !
+!
diff --git a/tests/topotests/bfd-topo3/r4/bgpd.conf b/tests/topotests/bfd-topo3/r4/bgpd.conf
new file mode 100644
index 0000000000..3e81008d5d
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r4/bgpd.conf
@@ -0,0 +1,16 @@
+router bgp 400
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:3::2 remote-as external
+ neighbor 2001:db8:3::2 bfd profile slow-tx
+ neighbor 2001:db8:1::1 remote-as external
+ neighbor 2001:db8:1::1 ebgp-multihop 3
+ neighbor 2001:db8:1::1 bfd profile slow-tx-mh
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::1 activate
+ neighbor 2001:db8:3::2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-topo3/r4/zebra.conf b/tests/topotests/bfd-topo3/r4/zebra.conf
new file mode 100644
index 0000000000..bf0cfcf42c
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r4/zebra.conf
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.4/32
+!
+interface r4-eth0
+ ip address 192.168.3.1/24
+ ipv6 address 2001:db8:3::1/64
+!
diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.dot b/tests/topotests/bfd-topo3/test_bfd_topo3.dot
new file mode 100644
index 0000000000..502cea11f2
--- /dev/null
+++ b/tests/topotests/bfd-topo3/test_bfd_topo3.dot
@@ -0,0 +1,73 @@
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+ label="bfd-topo3";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r4 [
+ shape=doubleoctagon
+ label="r4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n192.168.1.0/24\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n192.168.2.0/24\n2001:db8:2::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n192.168.3.0/24\n2001:db8:3::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0\n.1"];
+ r2 -- sw1 [label="eth0\n.2"];
+
+ r3 -- sw2 [label="eth0\n.1"];
+ r2 -- sw2 [label="eth1\n.2"];
+
+ r4 -- sw3 [label="eth0\n.1"];
+ r3 -- sw3 [label="eth2\n.2"];
+}
diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.jpg b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg
new file mode 100644
index 0000000000..6b532560bf
--- /dev/null
+++ b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg
Binary files differ
diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.py b/tests/topotests/bfd-topo3/test_bfd_topo3.py
new file mode 100644
index 0000000000..bcee338a92
--- /dev/null
+++ b/tests/topotests/bfd-topo3/test_bfd_topo3.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_topo3.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# 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_bfd_topo3.py: Test the FRR BFD daemon multi hop.
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+
+# 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
+
+
+class BFDTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(BFDTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.iteritems():
+ daemon_file = "{}/{}/bfdd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BFD, daemon_file)
+
+ daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
+
+ daemon_file = "{}/{}/bgpd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BGP, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def test_wait_bgp_convergence():
+ "Wait for BGP to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_loopback_route(router, iptype, route, proto):
+ "Wait until route is present on RIB for protocol."
+ logger.info('waiting route {} in {}'.format(route, router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ 'show {} route json'.format(iptype),
+ { route: [{ 'protocol': proto }] }
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" OSPF convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Wait for R1 <-> R2 convergence.
+ expect_loopback_route('r1', 'ip', '10.254.254.2/32', 'bgp')
+ # Wait for R1 <-> R3 convergence.
+ expect_loopback_route('r1', 'ip', '10.254.254.3/32', 'bgp')
+ # Wait for R1 <-> R4 convergence.
+ expect_loopback_route('r1', 'ip', '10.254.254.4/32', 'bgp')
+
+ # Wait for R2 <-> R1 convergence.
+ expect_loopback_route('r2', 'ip', '10.254.254.1/32', 'bgp')
+ # Wait for R2 <-> R3 convergence.
+ expect_loopback_route('r2', 'ip', '10.254.254.3/32', 'bgp')
+ # Wait for R2 <-> R4 convergence.
+ expect_loopback_route('r2', 'ip', '10.254.254.4/32', 'bgp')
+
+ # Wait for R3 <-> R1 convergence.
+ expect_loopback_route('r3', 'ip', '10.254.254.1/32', 'bgp')
+ # Wait for R3 <-> R2 convergence.
+ expect_loopback_route('r3', 'ip', '10.254.254.2/32', 'bgp')
+ # Wait for R3 <-> R4 convergence.
+ expect_loopback_route('r3', 'ip', '10.254.254.4/32', 'bgp')
+
+ # Wait for R4 <-> R1 convergence.
+ expect_loopback_route('r4', 'ip', '10.254.254.1/32', 'bgp')
+ # Wait for R4 <-> R2 convergence.
+ expect_loopback_route('r4', 'ip', '10.254.254.2/32', 'bgp')
+ # Wait for R4 <-> R3 convergence.
+ expect_loopback_route('r4', 'ip', '10.254.254.3/32', 'bgp')
+
+
+def test_wait_bfd_convergence():
+ "Wait for BFD to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("test BFD configurations")
+
+ def expect_bfd_configuration(router):
+ "Load JSON file and compare with 'show bfd peer json'"
+ logger.info('waiting BFD configuration on router {}'.format(router))
+ bfd_config = json.loads(open('{}/{}/bfd-peers.json'.format(CWD, router)).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ 'show bfd peers json',
+ bfd_config
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" BFD configuration failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_bfd_configuration('r1')
+ expect_bfd_configuration('r2')
+ expect_bfd_configuration('r3')
+ expect_bfd_configuration('r4')
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf b/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf
new file mode 100644
index 0000000000..8858e21496
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf
Binary files differ
diff --git a/tests/topotests/bgp-evpn-mh/hostd11/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd11/evpn.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd11/evpn.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd11/pim.conf b/tests/topotests/bgp-evpn-mh/hostd11/pim.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd11/pim.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd11/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd11/zebra.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd11/zebra.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd12/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd12/evpn.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd12/evpn.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd12/pim.conf b/tests/topotests/bgp-evpn-mh/hostd12/pim.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd12/pim.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd12/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd12/zebra.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd12/zebra.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd21/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd21/evpn.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd21/evpn.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd21/pim.conf b/tests/topotests/bgp-evpn-mh/hostd21/pim.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd21/pim.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd21/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd21/zebra.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd21/zebra.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd22/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd22/evpn.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd22/evpn.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd22/pim.conf b/tests/topotests/bgp-evpn-mh/hostd22/pim.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd22/pim.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd22/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd22/zebra.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd22/zebra.conf
diff --git a/tests/topotests/bgp-evpn-mh/spine1/evpn.conf b/tests/topotests/bgp-evpn-mh/spine1/evpn.conf
new file mode 100644
index 0000000000..2e26f60f44
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine1/evpn.conf
@@ -0,0 +1,17 @@
+frr defaults datacenter
+!
+router bgp 65001
+ bgp router-id 192.168.100.13
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.3.2 remote-as external
+ neighbor 192.168.4.2 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.2.2 activate
+ neighbor 192.168.3.2 activate
+ neighbor 192.168.4.2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/spine1/pim.conf b/tests/topotests/bgp-evpn-mh/spine1/pim.conf
new file mode 100644
index 0000000000..68e686e8c7
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine1/pim.conf
@@ -0,0 +1,18 @@
+ip pim rp 192.168.100.13
+ip pim spt-switchover infinity-and-beyond
+!
+int lo
+ ip pim
+!
+int spine1-eth0
+ ip pim
+!
+int spine1-eth1
+ ip pim
+!
+int spine1-eth2
+ ip pim
+!
+int spine1-eth3
+ ip pim
+!
diff --git a/tests/topotests/bgp-evpn-mh/spine1/zebra.conf b/tests/topotests/bgp-evpn-mh/spine1/zebra.conf
new file mode 100644
index 0000000000..80e9e5a263
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine1/zebra.conf
@@ -0,0 +1,15 @@
+int spine1-eth0
+ ip addr 192.168.1.1/24
+!
+int spine1-eth1
+ ip addr 192.168.2.1/24
+!
+int spine1-eth2
+ ip addr 192.168.3.1/24
+!
+int spine1-eth3
+ ip addr 192.168.4.1/24
+!
+int lo
+ ip addr 192.168.100.13/32
+ ip addr 192.168.100.100/32
diff --git a/tests/topotests/bgp-evpn-mh/spine2/evpn.conf b/tests/topotests/bgp-evpn-mh/spine2/evpn.conf
new file mode 100644
index 0000000000..ec2e789276
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine2/evpn.conf
@@ -0,0 +1,17 @@
+frr defaults datacenter
+!
+router bgp 65001
+ bgp router-id 192.168.100.14
+ no bgp ebgp-requires-policy
+ neighbor 192.168.5.2 remote-as external
+ neighbor 192.168.6.2 remote-as external
+ neighbor 192.168.7.2 remote-as external
+ neighbor 192.168.8.2 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.5.2 activate
+ neighbor 192.168.6.2 activate
+ neighbor 192.168.7.2 activate
+ neighbor 192.168.8.2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/spine2/pim.conf b/tests/topotests/bgp-evpn-mh/spine2/pim.conf
new file mode 100644
index 0000000000..c1566240e6
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine2/pim.conf
@@ -0,0 +1,18 @@
+ip pim rp 192.168.100.13
+ip pim spt-switchover infinity-and-beyond
+!
+int lo
+ ip pim
+!
+int spine2-eth0
+ ip pim
+!
+int spine2-eth1
+ ip pim
+!
+int spine2-eth2
+ ip pim
+!
+int spine2-eth3
+ ip pim
+!
diff --git a/tests/topotests/bgp-evpn-mh/spine2/zebra.conf b/tests/topotests/bgp-evpn-mh/spine2/zebra.conf
new file mode 100644
index 0000000000..1cd1df8c81
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine2/zebra.conf
@@ -0,0 +1,15 @@
+int spine2-eth0
+ ip addr 192.168.5.1/24
+!
+int spine2-eth1
+ ip addr 192.168.6.1/24
+!
+int spine2-eth2
+ ip addr 192.168.7.1/24
+!
+int spine2-eth3
+ ip addr 192.168.8.1/24
+!
+int lo
+ ip addr 192.168.100.14/32
+ ip addr 192.168.100.100/32
diff --git a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py
new file mode 100755
index 0000000000..fe28f79bd4
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py
@@ -0,0 +1,651 @@
+#!/usr/bin/env python
+
+#
+# test_evpn_mh.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc.
+# Anuradha Karuppiah
+#
+# 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_evpn_mh.py: Testing EVPN multihoming
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+import platform
+from functools import partial
+
+# 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
+
+#####################################################
+##
+## Network Topology Definition
+##
+## See topology picture at evpn-mh-topo-tests.pdf
+#####################################################
+
+
+class NetworkTopo(Topo):
+ '''
+ EVPN Multihoming Topology -
+ 1. Two level CLOS
+ 2. Two spine switches - spine1, spine2
+ 3. Two racks with Top-of-Rack switches per rack - tormx1, tormx2
+ 4. Two dual attached hosts per-rack - hostdx1, hostdx2
+ '''
+
+ def build(self, **_opts):
+ "Build function"
+
+ tgen = get_topogen(self)
+
+ tgen.add_router("spine1")
+ tgen.add_router("spine2")
+ tgen.add_router("torm11")
+ tgen.add_router("torm12")
+ tgen.add_router("torm21")
+ tgen.add_router("torm22")
+ tgen.add_router("hostd11")
+ tgen.add_router("hostd12")
+ tgen.add_router("hostd21")
+ tgen.add_router("hostd22")
+
+ # On main router
+ # First switch is for a dummy interface (for local network)
+
+
+ ##################### spine1 ########################
+ # spine1-eth0 is connected to torm11-eth0
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["torm11"])
+
+ # spine1-eth1 is connected to torm12-eth0
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["torm12"])
+
+ # spine1-eth2 is connected to torm21-eth0
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["torm21"])
+
+ # spine1-eth3 is connected to torm22-eth0
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["torm22"])
+
+ ##################### spine2 ########################
+ # spine2-eth0 is connected to torm11-eth1
+ switch = tgen.add_switch("sw5")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["torm11"])
+
+ # spine2-eth1 is connected to torm12-eth1
+ switch = tgen.add_switch("sw6")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["torm12"])
+
+ # spine2-eth2 is connected to torm21-eth1
+ switch = tgen.add_switch("sw7")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["torm21"])
+
+ # spine2-eth3 is connected to torm22-eth1
+ switch = tgen.add_switch("sw8")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["torm22"])
+
+ ##################### torm11 ########################
+ # torm11-eth2 is connected to hostd11-eth0
+ switch = tgen.add_switch("sw9")
+ switch.add_link(tgen.gears["torm11"])
+ switch.add_link(tgen.gears["hostd11"])
+
+ # torm11-eth3 is connected to hostd12-eth0
+ switch = tgen.add_switch("sw10")
+ switch.add_link(tgen.gears["torm11"])
+ switch.add_link(tgen.gears["hostd12"])
+
+ ##################### torm12 ########################
+ # torm12-eth2 is connected to hostd11-eth1
+ switch = tgen.add_switch("sw11")
+ switch.add_link(tgen.gears["torm12"])
+ switch.add_link(tgen.gears["hostd11"])
+
+ # torm12-eth3 is connected to hostd12-eth1
+ switch = tgen.add_switch("sw12")
+ switch.add_link(tgen.gears["torm12"])
+ switch.add_link(tgen.gears["hostd12"])
+
+ ##################### torm21 ########################
+ # torm21-eth2 is connected to hostd21-eth0
+ switch = tgen.add_switch("sw13")
+ switch.add_link(tgen.gears["torm21"])
+ switch.add_link(tgen.gears["hostd21"])
+
+ # torm21-eth3 is connected to hostd22-eth0
+ switch = tgen.add_switch("sw14")
+ switch.add_link(tgen.gears["torm21"])
+ switch.add_link(tgen.gears["hostd22"])
+
+ ##################### torm22 ########################
+ # torm22-eth2 is connected to hostd21-eth1
+ switch = tgen.add_switch("sw15")
+ switch.add_link(tgen.gears["torm22"])
+ switch.add_link(tgen.gears["hostd21"])
+
+ # torm22-eth3 is connected to hostd22-eth1
+ switch = tgen.add_switch("sw16")
+ switch.add_link(tgen.gears["torm22"])
+ switch.add_link(tgen.gears["hostd22"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+tor_ips = {"torm11" : "192.168.100.15", \
+ "torm12" : "192.168.100.16", \
+ "torm21" : "192.168.100.17", \
+ "torm22" : "192.168.100.18"}
+
+svi_ips = {"torm11" : "45.0.0.2", \
+ "torm12" : "45.0.0.3", \
+ "torm21" : "45.0.0.4", \
+ "torm22" : "45.0.0.5"}
+
+tor_ips_rack_1 = {"torm11" : "192.168.100.15", \
+ "torm12" : "192.168.100.16"}
+
+tor_ips_rack_2 = {"torm21" : "192.168.100.17", \
+ "torm22" : "192.168.100.18"}
+
+host_es_map = {"hostd11" : "03:44:38:39:ff:ff:01:00:00:01",
+ "hostd12" : "03:44:38:39:ff:ff:01:00:00:02",
+ "hostd21" : "03:44:38:39:ff:ff:02:00:00:01",
+ "hostd22" : "03:44:38:39:ff:ff:02:00:00:02"}
+
+def config_bond(node, bond_name, bond_members, bond_ad_sys_mac, br):
+ '''
+ Used to setup bonds on the TORs and hosts for MH
+ '''
+ node.run("ip link add dev %s type bond mode 802.3ad" % bond_name)
+ node.run("ip link set dev %s type bond lacp_rate 1" % bond_name)
+ node.run("ip link set dev %s type bond miimon 100" % bond_name)
+ node.run("ip link set dev %s type bond xmit_hash_policy layer3+4" % bond_name)
+ node.run("ip link set dev %s type bond min_links 1" % bond_name)
+ node.run("ip link set dev %s type bond ad_actor_system %s" %\
+ (bond_name, bond_ad_sys_mac))
+
+ for bond_member in bond_members:
+ node.run("ip link set dev %s down" % bond_member)
+ node.run("ip link set dev %s master %s" % (bond_member, bond_name))
+ node.run("ip link set dev %s up" % bond_member)
+
+ node.run("ip link set dev %s up" % bond_name)
+
+ # if bridge is specified add the bond as a bridge member
+ if br:
+ node.run(" ip link set dev %s master bridge" % bond_name)
+ node.run("/sbin/bridge link set dev %s priority 8" % bond_name)
+ node.run("/sbin/bridge vlan del vid 1 dev %s" % bond_name)
+ node.run("/sbin/bridge vlan del vid 1 untagged pvid dev %s" % bond_name)
+ node.run("/sbin/bridge vlan add vid 1000 dev %s" % bond_name)
+ node.run("/sbin/bridge vlan add vid 1000 untagged pvid dev %s"\
+ % bond_name)
+
+
+def config_mcast_tunnel_termination_device(node):
+ '''
+ The kernel requires a device to terminate VxLAN multicast tunnels
+ when EVPN-PIM is used for flooded traffic
+ '''
+ node.run("ip link add dev ipmr-lo type dummy")
+ node.run("ip link set dev ipmr-lo mtu 16000")
+ node.run("ip link set dev ipmr-lo mode dormant")
+ node.run("ip link set dev ipmr-lo up")
+
+
+def config_bridge(node):
+ '''
+ Create a VLAN aware bridge
+ '''
+ node.run("ip link add dev bridge type bridge stp_state 0")
+ node.run("ip link set dev bridge type bridge vlan_filtering 1")
+ node.run("ip link set dev bridge mtu 9216")
+ node.run("ip link set dev bridge type bridge ageing_time 1800")
+ node.run("ip link set dev bridge type bridge mcast_snooping 0")
+ node.run("ip link set dev bridge type bridge vlan_stats_enabled 1")
+ node.run("ip link set dev bridge up")
+ node.run("/sbin/bridge vlan add vid 1000 dev bridge")
+
+
+def config_vxlan(node, node_ip):
+ '''
+ Create a VxLAN device for VNI 1000 and add it to the bridge.
+ VLAN-1000 is mapped to VNI-1000.
+ '''
+ node.run("ip link add dev vx-1000 type vxlan id 1000 dstport 4789")
+ node.run("ip link set dev vx-1000 type vxlan nolearning")
+ node.run("ip link set dev vx-1000 type vxlan local %s" % node_ip)
+ node.run("ip link set dev vx-1000 type vxlan ttl 64")
+ node.run("ip link set dev vx-1000 mtu 9152")
+ node.run("ip link set dev vx-1000 type vxlan dev ipmr-lo group 239.1.1.100")
+ node.run("ip link set dev vx-1000 up")
+
+ # bridge attrs
+ node.run("ip link set dev vx-1000 master bridge")
+ node.run("/sbin/bridge link set dev vx-1000 neigh_suppress on")
+ node.run("/sbin/bridge link set dev vx-1000 learning off")
+ node.run("/sbin/bridge link set dev vx-1000 priority 8")
+ node.run("/sbin/bridge vlan del vid 1 dev vx-1000")
+ node.run("/sbin/bridge vlan del vid 1 untagged pvid dev vx-1000")
+ node.run("/sbin/bridge vlan add vid 1000 dev vx-1000")
+ node.run("/sbin/bridge vlan add vid 1000 untagged pvid dev vx-1000")
+
+
+def config_svi(node, svi_pip):
+ '''
+ Create an SVI for VLAN 1000
+ '''
+ node.run("ip link add link bridge name vlan1000 type vlan id 1000 protocol 802.1q")
+ node.run("ip addr add %s/24 dev vlan1000" % svi_pip)
+ node.run("ip link set dev vlan1000 up")
+ node.run("/sbin/sysctl net.ipv4.conf.vlan1000.arp_accept=1")
+ node.run("ip link add link vlan1000 name vlan1000-v0 type macvlan mode private")
+ node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.accept_dad=0")
+ node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.dad_transmits")
+ node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.dad_transmits=0")
+ node.run("ip link set dev vlan1000-v0 address 00:00:5e:00:01:01")
+ node.run("ip link set dev vlan1000-v0 up")
+ # metric 1024 is not working
+ node.run("ip addr add 45.0.0.1/24 dev vlan1000-v0")
+
+
+def config_tor(tor_name, tor, tor_ip, svi_pip):
+ '''
+ Create the bond/vxlan-bridge on the TOR which acts as VTEP and EPN-PE
+ '''
+ # create a device for terminating VxLAN multicast tunnels
+ config_mcast_tunnel_termination_device(tor)
+
+ # create a vlan aware bridge
+ config_bridge(tor)
+
+ # create vxlan device and add it to bridge
+ config_vxlan(tor, tor_ip)
+
+ # create hostbonds and add them to the bridge
+ if "torm1" in tor_name:
+ sys_mac = "44:38:39:ff:ff:01"
+ else:
+ sys_mac = "44:38:39:ff:ff:02"
+ bond_member = tor_name + "-eth2"
+ config_bond(tor, "hostbond1", [bond_member], sys_mac, "bridge")
+
+ bond_member = tor_name + "-eth3"
+ config_bond(tor, "hostbond2", [bond_member], sys_mac, "bridge")
+
+ # create SVI
+ config_svi(tor, svi_pip)
+
+
+def config_tors(tgen, tors):
+ for tor_name in tors:
+ tor = tgen.gears[tor_name]
+ config_tor(tor_name, tor, tor_ips.get(tor_name), svi_ips.get(tor_name))
+
+def compute_host_ip_mac(host_name):
+ host_id = host_name.split("hostd")[1]
+ host_ip = "45.0.0."+ host_id + "/24"
+ host_mac = "00:00:00:00:00:" + host_id
+
+ return host_ip, host_mac
+
+def config_host(host_name, host):
+ '''
+ Create the dual-attached bond on host nodes for MH
+ '''
+ bond_members = []
+ bond_members.append(host_name + "-eth0")
+ bond_members.append(host_name + "-eth1")
+ bond_name = "torbond"
+ config_bond(host, bond_name, bond_members, "00:00:00:00:00:00", None)
+
+ host_ip, host_mac = compute_host_ip_mac(host_name)
+ host.run("ip addr add %s dev %s" % (host_ip, bond_name))
+ host.run("ip link set dev %s address %s" % (bond_name, host_mac))
+
+
+def config_hosts(tgen, hosts):
+ for host_name in hosts:
+ host = tgen.gears[host_name]
+ config_host(host_name, host)
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(NetworkTopo, module.__name__)
+ tgen.start_topology()
+
+ krel = platform.release()
+ if topotest.version_cmp(krel, "4.19") < 0:
+ tgen.errors = "kernel 4.19 needed for multihoming tests"
+ pytest.skip(tgen.errors)
+
+ tors = []
+ tors.append("torm11")
+ tors.append("torm12")
+ tors.append("torm21")
+ tors.append("torm22")
+ config_tors(tgen, tors)
+
+ hosts = []
+ hosts.append("hostd11")
+ hosts.append("hostd12")
+ hosts.append("hostd21")
+ hosts.append("hostd22")
+ config_hosts(tgen, hosts)
+
+ # tgen.mininet_cli()
+ # This is a sample of configuration loading.
+ 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_PIM, os.path.join(CWD, "{}/pim.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/evpn.conf".format(rname))
+ )
+ tgen.start_router()
+ # tgen.mininet_cli()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def check_local_es(esi, vtep_ips, dut_name, down_vteps):
+ '''
+ Check if ES peers are setup correctly on local ESs
+ '''
+ peer_ips = []
+ if "torm1" in dut_name:
+ tor_ips_rack = tor_ips_rack_1
+ else:
+ tor_ips_rack = tor_ips_rack_2
+
+ for tor_name, tor_ip in tor_ips_rack.iteritems():
+ if dut_name not in tor_name:
+ peer_ips.append(tor_ip)
+
+ # remove down VTEPs from the peer check list
+ peer_set = set(peer_ips)
+ down_vtep_set = set(down_vteps)
+ peer_set = peer_set - down_vtep_set
+
+ vtep_set = set(vtep_ips)
+ diff = peer_set.symmetric_difference(vtep_set)
+
+ return (esi, diff) if diff else None
+
+
+def check_remote_es(esi, vtep_ips, dut_name, down_vteps):
+ '''
+ Verify list of PEs associated with a remote ES
+ '''
+ remote_ips = []
+
+ if "torm1" in dut_name:
+ tor_ips_rack = tor_ips_rack_2
+ else:
+ tor_ips_rack = tor_ips_rack_1
+
+ for tor_name, tor_ip in tor_ips_rack.iteritems():
+ remote_ips.append(tor_ip)
+
+ # remove down VTEPs from the remote check list
+ remote_set = set(remote_ips)
+ down_vtep_set = set(down_vteps)
+ remote_set = remote_set - down_vtep_set
+
+ vtep_set = set(vtep_ips)
+ diff = remote_set.symmetric_difference(vtep_set)
+
+ return (esi, diff) if diff else None
+
+def check_es(dut):
+ '''
+ Verify list of PEs associated all ESs, local and remote
+ '''
+ bgp_es = dut.vtysh_cmd("show bgp l2vp evpn es json")
+ bgp_es_json = json.loads(bgp_es)
+
+ result = None
+
+ expected_es_set = set([v for k, v in host_es_map.iteritems()])
+ curr_es_set = []
+
+ # check is ES content is correct
+ for es in bgp_es_json:
+ esi = es["esi"]
+ curr_es_set.append(esi)
+ types = es["type"]
+ vtep_ips = []
+ for vtep in es["vteps"]:
+ vtep_ips.append(vtep["vtep_ip"])
+
+ if "local" in types:
+ result = check_local_es(esi, vtep_ips, dut.name, [])
+ else:
+ result = check_remote_es(esi, vtep_ips, dut.name, [])
+
+ if result:
+ return result
+
+ # check if all ESs are present
+ curr_es_set = set(curr_es_set)
+ result = curr_es_set.symmetric_difference(expected_es_set)
+
+ return result if result else None
+
+def check_one_es(dut, esi, down_vteps):
+ '''
+ Verify list of PEs associated all ESs, local and remote
+ '''
+ bgp_es = dut.vtysh_cmd("show bgp l2vp evpn es %s json" % esi)
+ es = json.loads(bgp_es)
+
+ if not es:
+ return "esi %s not found" % esi
+
+ esi = es["esi"]
+ types = es["type"]
+ vtep_ips = []
+ for vtep in es["vteps"]:
+ vtep_ips.append(vtep["vtep_ip"])
+
+ if "local" in types:
+ result = check_local_es(esi, vtep_ips, dut.name, down_vteps)
+ else:
+ result = check_remote_es(esi, vtep_ips, dut.name, down_vteps)
+
+ return result
+
+def test_evpn_es():
+ '''
+ Two ES are setup on each rack. This test checks if -
+ 1. ES peer has been added to the local ES (via Type-1/EAD route)
+ 2. The remote ESs are setup with the right list of PEs (via Type-1)
+ '''
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ dut_name = "torm11"
+ dut = tgen.gears[dut_name]
+ test_fn = partial(check_es, dut)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+
+ assertmsg = '"{}" ES content incorrect'.format(dut_name)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+def test_evpn_ead_update():
+ '''
+ Flap a host link one the remote rack and check if the EAD updates
+ are sent/processed for the corresponding ESI
+ '''
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # dut on rack1 and host link flap on rack2
+ dut_name = "torm11"
+ dut = tgen.gears[dut_name]
+
+ remote_tor_name = "torm21"
+ remote_tor = tgen.gears[remote_tor_name]
+
+ host_name = "hostd21"
+ host = tgen.gears[host_name]
+ esi = host_es_map.get(host_name)
+
+ # check if the VTEP list is right to start with
+ down_vteps = []
+ test_fn = partial(check_one_es, dut, esi, down_vteps)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" ES content incorrect'.format(dut_name)
+ assert result is None, assertmsg
+
+ # down a remote host link and check if the EAD withdraw is rxed
+ # Note: LACP is not working as expected so I am temporarily shutting
+ # down the link on the remote TOR instead of the remote host
+ remote_tor.run("ip link set dev %s-%s down" % (remote_tor_name, "eth2"))
+ down_vteps.append(tor_ips.get(remote_tor_name))
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" ES incorrect after remote link down'.format(dut_name)
+ assert result is None, assertmsg
+
+ # bring up remote host link and check if the EAD update is rxed
+ down_vteps.remove(tor_ips.get(remote_tor_name))
+ remote_tor.run("ip link set dev %s-%s up" % (remote_tor_name, "eth2"))
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" ES incorrect after remote link flap'.format(dut_name)
+ assert result is None, assertmsg
+
+ # tgen.mininet_cli()
+
+def check_mac(dut, vni, mac, m_type, esi, intf):
+ '''
+ checks if mac is present and if desination matches the one provided
+ '''
+
+ out = dut.vtysh_cmd("show evpn mac vni %d mac %s json" % (vni, mac))
+
+ mac_js = json.loads(out)
+ for mac, info in mac_js.iteritems():
+ tmp_esi = info.get("esi", "")
+ tmp_m_type = info.get("type", "")
+ tmp_intf = info.get("intf", "") if tmp_m_type == "local" else ""
+ if tmp_esi == esi and tmp_m_type == m_type and intf == intf:
+ return None
+
+ return "invalid vni %d mac %s out %s" % (vni, mac, mac_js)
+
+def test_evpn_mac():
+ '''
+ 1. Add a MAC on hostd11 and check if the MAC is synced between
+ torm11 and torm12. And installed as a local MAC.
+ 2. Add a MAC on hostd21 and check if the MAC is installed as a
+ remote MAC on torm11 and torm12
+ '''
+
+ tgen = get_topogen()
+
+ local_host = tgen.gears["hostd11"]
+ remote_host = tgen.gears["hostd21"]
+ tors = []
+ tors.append(tgen.gears["torm11"])
+ tors.append(tgen.gears["torm12"])
+
+ # ping the anycast gw from the local and remote hosts to populate
+ # the mac address on the PEs
+ local_host.run("arping -I torbond -c 1 45.0.0.1")
+ remote_host.run("arping -I torbond -c 1 45.0.0.1")
+
+ vni = 1000
+
+ # check if the rack-1 host MAC is present on all rack-1 PEs
+ # and points to local access port
+ m_type = "local"
+ _, mac = compute_host_ip_mac(local_host.name)
+ esi = host_es_map.get(local_host.name)
+ intf = "hostbond1"
+
+ for tor in tors:
+ test_fn = partial(check_mac, tor, vni, mac, m_type, esi, intf)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" local MAC content incorrect'.format(tor.name)
+ assert result is None, assertmsg
+
+ # check if the rack-2 host MAC is present on all rack-1 PEs
+ # and points to the remote ES destination
+ m_type = "remote"
+ _, mac = compute_host_ip_mac(remote_host.name)
+ esi = host_es_map.get(remote_host.name)
+ intf = ""
+
+ for tor in tors:
+ test_fn = partial(check_mac, tor, vni, mac, m_type, esi, intf)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" remote MAC content incorrect'.format(tor.name)
+ assert result is None, assertmsg
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp-evpn-mh/torm11/evpn.conf b/tests/topotests/bgp-evpn-mh/torm11/evpn.conf
new file mode 100644
index 0000000000..01f4b65704
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm11/evpn.conf
@@ -0,0 +1,21 @@
+!
+frr defaults datacenter
+!
+debug bgp evpn mh es
+debug bgp evpn mh route
+debug bgp zebra
+!
+!
+router bgp 65002
+ bgp router-id 192.168.100.15
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.5.1 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.1.1 activate
+ neighbor 192.168.5.1 activate
+ advertise-all-vni
+ advertise-svi-ip
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm11/pim.conf b/tests/topotests/bgp-evpn-mh/torm11/pim.conf
new file mode 100644
index 0000000000..fbba735873
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm11/pim.conf
@@ -0,0 +1,13 @@
+!
+ip pim rp 192.168.100.13 239.1.1.0/24
+ip pim spt-switchover infinity-and-beyond
+!
+interface lo
+ ip igmp
+ ip pim
+!
+interface torm11-eth0
+ ip pim
+!
+interface torm11-eth1
+ ip pim
diff --git a/tests/topotests/bgp-evpn-mh/torm11/zebra.conf b/tests/topotests/bgp-evpn-mh/torm11/zebra.conf
new file mode 100644
index 0000000000..ee4e87e1c2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm11/zebra.conf
@@ -0,0 +1,23 @@
+debug zebra evpn mh es
+debug zebra evpn mh mac
+debug zebra evpn mh neigh
+debug zebra evpn mh nh
+debug zebra vxlan
+!
+int torm11-eth0
+ ip addr 192.168.1.2/24
+!
+int torm11-eth1
+ ip addr 192.168.5.2/24
+!
+int lo
+ ip addr 192.168.100.15/32
+!
+interface hostbond1
+ evpn mh es-id 1
+ evpn mh es-sys-mac 44:38:39:ff:ff:01
+!
+interface hostbond2
+ evpn mh es-id 2
+ evpn mh es-sys-mac 44:38:39:ff:ff:01
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm12/evpn.conf b/tests/topotests/bgp-evpn-mh/torm12/evpn.conf
new file mode 100644
index 0000000000..2c13024bbc
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm12/evpn.conf
@@ -0,0 +1,21 @@
+!
+frr defaults datacenter
+!
+debug bgp evpn mh es
+debug bgp evpn mh route
+debug bgp zebra
+!
+!
+router bgp 65003
+ bgp router-id 192.168.100.16
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.6.1 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.2.1 activate
+ neighbor 192.168.6.1 activate
+ advertise-all-vni
+ advertise-svi-ip
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm12/pim.conf b/tests/topotests/bgp-evpn-mh/torm12/pim.conf
new file mode 100644
index 0000000000..3dd63b44ca
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm12/pim.conf
@@ -0,0 +1,13 @@
+!
+ip pim rp 192.168.100.13 239.1.1.0/24
+ip pim spt-switchover infinity-and-beyond
+!
+interface lo
+ ip igmp
+ ip pim
+!
+interface torm12-eth0
+ ip pim
+!
+interface torm12-eth1
+ ip pim
diff --git a/tests/topotests/bgp-evpn-mh/torm12/zebra.conf b/tests/topotests/bgp-evpn-mh/torm12/zebra.conf
new file mode 100644
index 0000000000..736af4159e
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm12/zebra.conf
@@ -0,0 +1,23 @@
+debug zebra evpn mh es
+debug zebra evpn mh mac
+debug zebra evpn mh neigh
+debug zebra evpn mh nh
+debug zebra vxlan
+!
+int torm12-eth0
+ ip addr 192.168.2.2/24
+!
+int torm12-eth1
+ ip addr 192.168.6.2/24
+!
+int lo
+ ip addr 192.168.100.16/32
+!
+interface hostbond1
+ evpn mh es-id 1
+ evpn mh es-sys-mac 44:38:39:ff:ff:01
+!
+interface hostbond2
+ evpn mh es-id 2
+ evpn mh es-sys-mac 44:38:39:ff:ff:01
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm21/evpn.conf b/tests/topotests/bgp-evpn-mh/torm21/evpn.conf
new file mode 100644
index 0000000000..2a2ba061c6
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm21/evpn.conf
@@ -0,0 +1,21 @@
+!
+frr defaults datacenter
+!
+debug bgp evpn mh es
+debug bgp evpn mh route
+debug bgp zebra
+!
+!
+router bgp 65004
+ bgp router-id 192.168.100.17
+ no bgp ebgp-requires-policy
+ neighbor 192.168.3.1 remote-as external
+ neighbor 192.168.7.1 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.3.1 activate
+ neighbor 192.168.7.1 activate
+ advertise-all-vni
+ advertise-svi-ip
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm21/pim.conf b/tests/topotests/bgp-evpn-mh/torm21/pim.conf
new file mode 100644
index 0000000000..71aa91a06d
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm21/pim.conf
@@ -0,0 +1,13 @@
+!
+ip pim rp 192.168.100.13 239.1.1.0/24
+ip pim spt-switchover infinity-and-beyond
+!
+interface lo
+ ip igmp
+ ip pim
+!
+interface torm21-eth0
+ ip pim
+!
+interface torm21-eth1
+ ip pim
diff --git a/tests/topotests/bgp-evpn-mh/torm21/zebra.conf b/tests/topotests/bgp-evpn-mh/torm21/zebra.conf
new file mode 100644
index 0000000000..0ebe6f2d95
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm21/zebra.conf
@@ -0,0 +1,23 @@
+debug zebra evpn mh es
+debug zebra evpn mh mac
+debug zebra evpn mh neigh
+debug zebra evpn mh nh
+debug zebra vxlan
+!
+int torm21-eth0
+ ip addr 192.168.3.2/24
+!
+int torm21-eth1
+ ip addr 192.168.7.2/24
+!
+int lo
+ ip addr 192.168.100.17/32
+!
+interface hostbond1
+ evpn mh es-id 1
+ evpn mh es-sys-mac 44:38:39:ff:ff:02
+!
+interface hostbond2
+ evpn mh es-id 2
+ evpn mh es-sys-mac 44:38:39:ff:ff:02
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm22/evpn.conf b/tests/topotests/bgp-evpn-mh/torm22/evpn.conf
new file mode 100644
index 0000000000..b4f4f1dc25
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm22/evpn.conf
@@ -0,0 +1,21 @@
+!
+frr defaults datacenter
+!
+debug bgp evpn mh es
+debug bgp evpn mh route
+debug bgp zebra
+!
+!
+router bgp 65005
+ bgp router-id 192.168.100.18
+ no bgp ebgp-requires-policy
+ neighbor 192.168.4.1 remote-as external
+ neighbor 192.168.8.1 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.4.1 activate
+ neighbor 192.168.8.1 activate
+ advertise-all-vni
+ advertise-svi-ip
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm22/pim.conf b/tests/topotests/bgp-evpn-mh/torm22/pim.conf
new file mode 100644
index 0000000000..46f330f5cd
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm22/pim.conf
@@ -0,0 +1,13 @@
+!
+ip pim rp 192.168.100.13 239.1.1.0/24
+ip pim spt-switchover infinity-and-beyond
+!
+interface lo
+ ip igmp
+ ip pim
+!
+interface torm22-eth0
+ ip pim
+!
+interface torm22-eth1
+ ip pim
diff --git a/tests/topotests/bgp-evpn-mh/torm22/zebra.conf b/tests/topotests/bgp-evpn-mh/torm22/zebra.conf
new file mode 100644
index 0000000000..356d8a43e7
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm22/zebra.conf
@@ -0,0 +1,23 @@
+debug zebra evpn mh es
+debug zebra evpn mh mac
+debug zebra evpn mh neigh
+debug zebra evpn mh nh
+debug zebra vxlan
+!
+int torm22-eth0
+ ip addr 192.168.4.2/24
+!
+int torm22-eth1
+ ip addr 192.168.8.2/24
+!
+int lo
+ ip addr 192.168.100.18/32
+!
+interface hostbond1
+ evpn mh es-id 1
+ evpn mh es-sys-mac 44:38:39:ff:ff:02
+!
+interface hostbond2
+ evpn mh es-id 2
+ evpn mh es-sys-mac 44:38:39:ff:ff:02
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
index d9f2182aa0..2937504244 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
@@ -3,12 +3,11 @@
"type":"L2",
"vrf":"default",
"vxlanInterface":"vxlan101",
- "ifindex":5,
"vtepIp":"10.10.10.10",
"mcastGroup":"0.0.0.0",
"advertiseGatewayMacip":"No",
"numMacs":5,
- "numArpNd":2,
+ "numArpNd":3,
"numRemoteVteps":[
"10.30.30.30"
]
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
index 938ec7bca9..e2699475c9 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
@@ -3,8 +3,6 @@ log file zebra.log
!
interface lo
ip address 10.10.10.10/32
-interface PE1-eth0
- ip address 10.10.1.1/24
interface PE1-eth1
ip address 10.20.1.1/24
!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
index 13255ab4f2..0853147a00 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
@@ -3,12 +3,11 @@
"type":"L2",
"vrf":"default",
"vxlanInterface":"vxlan101",
- "ifindex":5,
"vtepIp":"10.30.30.30",
"mcastGroup":"0.0.0.0",
"advertiseGatewayMacip":"No",
"numMacs":5,
- "numArpNd":2,
+ "numArpNd":3,
"numRemoteVteps":[
"10.10.10.10"
]
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
index 07b83f6395..9738916ab0 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
@@ -3,6 +3,4 @@ interface lo
ip address 10.30.30.30/32
interface PE2-eth0
ip address 10.20.2.3/24
-interface PE2-eth1
- ip address 10.10.1.3/24
!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
index ad72540185..90144f5c66 100755
--- a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
@@ -29,6 +29,7 @@ import os
import sys
import json
from functools import partial
+from time import sleep
import pytest
# Save the Current Working Directory to find configuration files.
@@ -97,6 +98,7 @@ def setup_module(mod):
# set up PE bridges with the EVPN member interfaces facing the CE hosts
pe1.run("ip link add name br101 type bridge stp_state 0")
+ pe1.run("ip addr add 10.10.1.1/24 dev br101")
pe1.run("ip link set dev br101 up")
pe1.run(
"ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.10.10.10 nolearning"
@@ -106,6 +108,7 @@ def setup_module(mod):
pe1.run("ip link set dev PE1-eth0 master br101")
pe2.run("ip link add name br101 type bridge stp_state 0")
+ pe2.run("ip addr add 10.10.1.3/24 dev br101")
pe2.run("ip link set dev br101 up")
pe2.run(
"ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.30.30.30 nolearning"
@@ -142,6 +145,15 @@ def teardown_module(mod):
tgen.stop_topology()
+def show_vni_json_elide_ifindex(pe, vni, expected):
+ output_json = pe.vtysh_cmd("show evpn vni {} json".format(vni), isjson=True)
+
+ if "ifindex" in output_json:
+ output_json.pop("ifindex")
+
+ return topotest.json_cmp(output_json, expected)
+
+
def test_pe1_converge_evpn():
"Wait for protocol convergence"
@@ -154,9 +166,7 @@ def test_pe1_converge_evpn():
json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name)
expected = json.loads(open(json_file).read())
- test_func = partial(
- topotest.router_json_cmp, pe1, "show evpn vni 101 json", expected
- )
+ test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected)
_, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
assert result is None, assertmsg
@@ -175,9 +185,7 @@ def test_pe2_converge_evpn():
json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name)
expected = json.loads(open(json_file).read())
- test_func = partial(
- topotest.router_json_cmp, pe2, "show evpn vni 101 json", expected
- )
+ test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected)
_, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
assertmsg = '"{}" JSON output mismatches'.format(pe2.name)
assert result is None, assertmsg
@@ -194,7 +202,7 @@ def mac_learn_test(host, local):
mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac))
mac_output_json = json.loads(mac_output)
assertmsg = "Local MAC output does not match interface mac {}".format(mac)
- assert mac_output_json[mac]["type"] == "local"
+ assert mac_output_json[mac]["type"] == "local", assertmsg
def mac_test_local_remote(local, remote):
@@ -275,6 +283,103 @@ def test_local_remote_mac_pe2():
# Memory leak test template
+def ip_learn_test(tgen, host, local, remote, ip_addr):
+ "check the host IP gets learned by the VNI"
+ host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name))
+ int_lines = host_output.splitlines()
+ mac_line = int_lines[7].split(": ")
+ mac = mac_line[1]
+ print(host_output)
+
+ # check we have a local association between the MAC and IP
+ local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac))
+ print(local_output)
+ local_output_json = json.loads(local_output)
+ mac_type = local_output_json[mac]["type"]
+ assertmsg = "Failed to learn local IP address on host {}".format(host.name)
+ assert local_output_json[mac]["neighbors"] != "none", assertmsg
+ learned_ip = local_output_json[mac]["neighbors"]["active"][0]
+
+ assertmsg = "local learned mac wrong type: {} ".format(mac_type)
+ assert mac_type == "local", assertmsg
+
+ assertmsg = "learned address mismatch with configured address host: {} learned: {}".format(
+ ip_addr, learned_ip
+ )
+ assert ip_addr == learned_ip, assertmsg
+
+ # now lets check the remote
+ count = 0
+ converged = False
+ while count < 30:
+ remote_output = remote.vtysh_cmd(
+ "show evpn mac vni 101 mac {} json".format(mac)
+ )
+ print(remote_output)
+ remote_output_json = json.loads(remote_output)
+ type = remote_output_json[mac]["type"]
+ if not remote_output_json[mac]["neighbors"] == "none":
+ # due to a kernel quirk, learned IPs can be inactive
+ if (
+ remote_output_json[mac]["neighbors"]["active"]
+ or remote_output_json[mac]["neighbors"]["inactive"]
+ ):
+ converged = True
+ break
+ count += 1
+ sleep(1)
+
+ print("tries: {}".format(count))
+ assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac)
+ # some debug for this failure
+ if not converged == True:
+ log_output = remote.run("cat zebra.log")
+ print(log_output)
+
+ assert converged == True, assertmsg
+ if remote_output_json[mac]["neighbors"]["active"]:
+ learned_ip = remote_output_json[mac]["neighbors"]["active"][0]
+ else:
+ learned_ip = remote_output_json[mac]["neighbors"]["inactive"][0]
+ assertmsg = "remote learned mac wrong type: {} ".format(type)
+ assert type == "remote", assertmsg
+
+ assertmsg = "remote learned address mismatch with configured address host: {} learned: {}".format(
+ ip_addr, learned_ip
+ )
+ assert ip_addr == learned_ip, assertmsg
+
+
+def test_ip_pe1_learn():
+ "run the IP learn test for PE1"
+
+ tgen = get_topogen()
+ host1 = tgen.gears["host1"]
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ pe2.vtysh_cmd("debug zebra vxlan")
+ pe2.vtysh_cmd("debug zebra kernel")
+ # lets populate that arp cache
+ host1.run("ping -c1 10.10.1.1")
+ ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55")
+ # tgen.mininet_cli()
+
+
+def test_ip_pe2_learn():
+ "run the IP learn test for PE2"
+
+ tgen = get_topogen()
+ host2 = tgen.gears["host2"]
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ pe1.vtysh_cmd("debug zebra vxlan")
+ pe1.vtysh_cmd("debug zebra kernel")
+ # lets populate that arp cache
+ host2.run("ping -c1 10.10.1.3")
+ ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56")
+ # tgen.mininet_cli()
+
+
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
index c1bb7e3d15..2712e54f12 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
index c889a4c596..69305512cb 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
index 36dd97190e..3ad95c3612 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
index 33041262f6..502c4c8b2f 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
index 29b9f0da6c..2f7de073c3 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
index e09b505ee4..720d06dbf1 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
index c3309d8c75..b81cd33c4f 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce1
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
index 54401bfb2f..f18e5b852e 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce2
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
index f742fede1a..54a0933588 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce3
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
index 91311f32c5..5289628480 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce4
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
index a9549e8fee..5da53ae1e7 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debugging
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
index cda6d9429a..e4a6b8e32c 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debugging
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
index e2a8de7db7..a861469c7a 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
index 7b267a6ee1..480f95954e 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debug
diff --git a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
index 2f8759f960..06bdc31f8c 100644
--- a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
+++ b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
@@ -1,5 +1,4 @@
log stdout notifications
-log monitor notifications
log commands
!
router bgp 1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
index b3fe5ff23d..ada354bd62 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
index fbb6a65d61..4932d63d4f 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
index d61f776f3d..1a5e41aae6 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
index 626d8227e7..a38afd632f 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
index 8c75a39efa..dbeb2c4665 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
index 38f8758cbc..ae1787718c 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/evpn_type5_test_topo1/__init__.py b/tests/topotests/evpn_type5_test_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/__init__.py
diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json
new file mode 100644
index 0000000000..14842da326
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json
@@ -0,0 +1,887 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 30,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"10.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"20.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"20::1/128",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"30.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ },
+ {
+ "network":"30::1/128",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ }
+ ]
+ },
+ "e1": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ },
+ "d2": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d1": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d2-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d2-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
new file mode 100644
index 0000000000..14842da326
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
@@ -0,0 +1,887 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 30,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"10.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"20.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"20::1/128",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"30.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ },
+ {
+ "network":"30::1/128",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ }
+ ]
+ },
+ "e1": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ },
+ "d2": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d1": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d2-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d2-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
new file mode 100755
index 0000000000..941593e51f
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
@@ -0,0 +1,1047 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+"""
+Following tests are covered to test EVPN-Type5 functionality:
+1. In absence of an overlay index all IP-Prefixes(RT-5)
+ are advertised with default values for below parameters:
+ --> Ethernet Tag ID = GW IP address = ESI=0
+2. EVPN CLI output and JSON format validation.
+3. RT verification(auto)
+"""
+
+import os
+import re
+import sys
+import json
+import time
+import pytest
+import platform
+from copy import deepcopy
+from time import sleep
+
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topotest import version_cmp
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ start_router_daemons,
+ kill_router_daemons,
+ create_static_routes,
+ create_vrf_cfg,
+ create_route_maps,
+ create_interface_in_kernel,
+ check_router_status,
+ configure_vxlan,
+ configure_brctl,
+ apply_raw_config,
+ verify_vrf_vni,
+ verify_cli_json
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp,
+ verify_best_path_as_per_bgp_attribute,
+ verify_attributes_for_evpn_routes,
+ verify_evpn_routes
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/evpn_type5_chaos_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Reading the data from JSON File for topology creation
+# Global variables
+TCPDUMP_FILE = "evpn_log.txt"
+LOGDIR = "/tmp/topotests/"
+NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"}
+NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"}
+NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"}
+NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"}
+NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"}
+NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"}
+NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+VNI_1 = 75100
+VNI_2 = 75200
+VNI_3 = 75300
+MAC_1 = "00:80:48:ba:d1:00"
+MAC_2 = "00:80:48:ba:d1:01"
+MAC_3 = "00:80:48:ba:d1:02"
+BRCTL_1 = "br100"
+BRCTL_2 = "br200"
+BRCTL_3 = "br300"
+VXLAN_1 = "vxlan75100"
+VXLAN_2 = "vxlan75200"
+VXLAN_3 = "vxlan75300"
+BRIDGE_INTF1 = "120.0.0.1"
+BRIDGE_INTF2 = "120.0.0.2"
+BRIDGE_INTF3 = "120.0.0.3"
+MULTICAST_MAC1 = "01:00:5e:00:52:02"
+
+VXLAN = {
+ "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vxlan_id": [75100, 75200, 75300],
+ "dstport": 4789,
+ "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3},
+ "learning": "no",
+}
+BRCTL = {
+ "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3],
+ "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vrf": ["RED", "BLUE", "GREEN"],
+ "stp": [0, 0, 0],
+}
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('EVPN tests will not run (have kernel "{}", '
+ 'but it requires >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Pre-requisite config for testsuite")
+ prerequisite_config_for_test_suite(tgen)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def prerequisite_config_for_test_suite(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Configure vxlan, bridge interface")
+ for dut in ["e1", "d1", "d2"]:
+ step("[DUT: ]Configure vxlan")
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default routes")
+ add_default_routes(tgen)
+
+
+def add_default_routes(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Add default routes..")
+
+ default_routes = {
+ "e1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d2": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, default_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_verify_overlay_index_p1(request):
+ """
+ In absence of an overlay index all IP-Prefixes(RT-5)
+ are advertised with default values for below parameters:
+ --> Ethernet Tag ID = GW IP address = ESI=0
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Following steps are taken care in base config:")
+ step(
+ "Configure BGP neighborship for both address families"
+ "(IPv4 & IPv6) between Edge-1 and VFN routers(R1 and R2)"
+ )
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify: Prefixes are received in all VRFs on Edge-1 router.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that EVPN routes, received on DCG-1 and DCG-2 do not "
+ "carry any overlay index and these indexes are set to default "
+ "value=0. "
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d1", input_routes, ethTag=0
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes, ethTag=0
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_cli_json_available_p1(request):
+ """
+ EVPN CLI output and JSON format validation.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Need to verify below CLIs and associated JSON format " "outputs:")
+
+ input_dict = {
+ "e1": {
+ "cli": [
+ "show evpn vni detail",
+ "show bgp l2vpn evpn all overlay",
+ "show bgp l2vpn evpn vni"
+ ]
+ }
+ }
+
+ result = verify_cli_json(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_RT_verification_auto_p0(request):
+ """
+ RT verification(auto)
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise overlapping prefixes from VNFs R1 and R2 in all VRFs "
+ "RED, GREEN and BLUE 100.1.1.1/32 and 100::100/128"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that Edge-1 receives same prefixes in all 3 VRFs via "
+ "corresponding next-hop in associated VRF sh bgp vrf all"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure 4-byte local AS number on Edge-1 and establish EVPN "
+ "neighborship with DCG-1 & DCG-2."
+ )
+
+ topo_local = deepcopy(topo)
+
+ step("Delete BGP config for vrf RED.")
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_2 = {}
+ for dut in ["e1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ INDEX = [0, 1, 2, 3]
+ VRFS = ["RED", "BLUE", "GREEN", None]
+ AS_NUM = [100, 100, 100, 100]
+
+ for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM):
+ topo_local["routers"][dut]["bgp"][index]["local_as"] = 4294967293
+ if vrf:
+ temp[dut]["bgp"].append(
+ {"local_as": as_num, "vrf": vrf, "delete": True}
+ )
+ else:
+ temp[dut]["bgp"].append({"local_as": as_num, "delete": True})
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = create_router_bgp(tgen, topo_local["routers"])
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": VNI_1},
+ {"name": "BLUE", "vni": VNI_2},
+ {"name": "GREEN", "vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that all overlapping prefixes across different VRFs are "
+ "advertised in EVPN with unique RD value(auto derived)."
+ )
+ step(
+ "Verify that FRR uses only the lower 2 bytes of ASN+VNI for auto "
+ "derived RT value."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+ input_routes_2 = {
+ "r2": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "BLUE"}]}
+ }
+ input_routes_3 = {
+ "r2": {
+ "static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "GREEN"}]
+ }
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_1, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_1, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_2, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_2, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_3, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_3, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-1(iBGP peer) automatically imports the prefixes"
+ " from EVPN address-family to respective VRFs."
+ )
+ step(
+ "Verify if DCG-2(eBGP peer) automatically imports the prefixes "
+ "from EVPN address-family to respective VRFs or not."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Change the VNI number for all 3 VRFs on Edge-1 as:"
+ "RED : 75400, GREEN: 75500, BLUE: 75600"
+ )
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": 75400},
+ {"name": "BLUE", "vni": 75500},
+ {"name": "GREEN", "vni": 75600},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete configured vxlan")
+ dut = "e1"
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configured vxlan")
+ VXLAN["vxlan_id"] = [75400, 75500, 75600]
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number.."
+ )
+
+ input_dict = {
+ "e1": {
+ "vrfs": [
+ {"RED": {"vni": 75400}},
+ {"BLUE": {"vni": 75500}},
+ {"GREEN": {"vni": 75600}},
+ ]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on DCG-2 that prefixes are not imported from EVPN "
+ "address-family to VRFs as RT values are different on sending("
+ "edge-1) and receiving(DCG-2) end."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Revert back to original VNI number for all 3 VRFs on Edge-1 "
+ "as: RED : 75100, GREEN: 75200, BLUE: 75300"
+ )
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": 75400},
+ {"name": "BLUE", "no_vni": 75500},
+ {"name": "GREEN", "no_vni": 75600},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": VNI_1},
+ {"name": "BLUE", "vni": VNI_2},
+ {"name": "GREEN", "vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete configured vxlan")
+ dut = "e1"
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configured vxlan")
+ VXLAN["vxlan_id"] = [75100, 75200, 75300]
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number."
+ )
+ step(
+ "Verify that DCG-1(iBGP peer) automatically imports the prefixes"
+ " from EVPN address-family to respective VRFs."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Test with smaller VNI numbers (1-75000)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": VNI_1}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 111}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that DCG-2 receives EVPN prefixes along with auto "
+ "derived RT values(based on smaller VNI numbers)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n "
+ "Malfaromed Auto-RT value accepted: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Configure VNI number more than boundary limit (16777215)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 111}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 16777215}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("CLI error for malformed VNI.")
+ input_dict = {
+ "e1": {
+ "vrfs": [{"RED": {"vni": 16777215, "routerMac": "None", "state": "Down"}}]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n "
+ "Malfaromed Auto-RT value accepted: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Un-configure VNI number more than boundary limit (16777215)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 16777215}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
new file mode 100755
index 0000000000..8892d13eff
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
@@ -0,0 +1,2117 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+"""
+Following tests are covered to test EVPN-Type5 functionality:
+
+1. RD verification (manual/auto).
+2. RT verification(manual)
+3. In an active/standby EVPN implementation, if active DCG goes down,
+ secondary takes over.
+4. EVPN routes are advertised/withdrawn, based on VNFs
+ advertising/withdrawing IP prefixes.
+5. Route-map operations for EVPN address family.
+6. BGP attributes for EVPN address-family.
+"""
+
+import os
+import re
+import sys
+import json
+import time
+import pytest
+import platform
+from copy import deepcopy
+from time import sleep
+
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topotest import version_cmp
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ create_route_maps,
+ verify_cli_json,
+ start_router_daemons,
+ kill_router_daemons,
+ create_static_routes,
+ stop_router,
+ start_router,
+ create_vrf_cfg,
+ check_router_status,
+ apply_raw_config,
+ configure_vxlan,
+ configure_brctl,
+ verify_vrf_vni,
+ create_interface_in_kernel
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp,
+ verify_best_path_as_per_bgp_attribute,
+ verify_attributes_for_evpn_routes,
+ verify_evpn_routes
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/evpn_type5_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"}
+NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"}
+NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"}
+NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"}
+NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"}
+NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"}
+NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+VNI_1 = 75100
+VNI_2 = 75200
+VNI_3 = 75300
+MAC_1 = "00:80:48:ba:d1:00"
+MAC_2 = "00:80:48:ba:d1:01"
+MAC_3 = "00:80:48:ba:d1:02"
+BRCTL_1 = "br100"
+BRCTL_2 = "br200"
+BRCTL_3 = "br300"
+VXLAN_1 = "vxlan75100"
+VXLAN_2 = "vxlan75200"
+VXLAN_3 = "vxlan75300"
+BRIDGE_INTF1 = "120.0.0.1"
+BRIDGE_INTF2 = "120.0.0.2"
+BRIDGE_INTF3 = "120.0.0.3"
+
+VXLAN = {
+ "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vxlan_id": [75100, 75200, 75300],
+ "dstport": 4789,
+ "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3},
+ "learning": "no",
+}
+BRCTL = {
+ "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3],
+ "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vrf": ["RED", "BLUE", "GREEN"],
+ "stp": [0, 0, 0],
+}
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('EVPN tests will not run (have kernel "{}", '
+ 'but it requires >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Pre-requisite config for testsuite")
+ prerequisite_config_for_test_suite(tgen)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def prerequisite_config_for_test_suite(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Configure vxlan, bridge interface")
+ for dut in ["e1", "d1", "d2"]:
+ step("[DUT: ]Configure vxlan")
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step("Configure default routes")
+ add_default_routes(tgen)
+
+
+def add_default_routes(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Add default routes..")
+
+ default_routes = {
+ "e1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d2": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, default_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_RD_verification_manual_and_auto_p0(request):
+ """
+ RD verification (manual/auto).
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step(
+ "Advertise vrf RED's routes in EVPN address family from Edge-1 router"
+ ", without manual configuration of RD."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify on DCG-1 and DCG-2:")
+ step("EVPN route for 10.1.1.1/32 has auto-assigned RD value.")
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Configure RD for vrf RED manually as 50.50.50.50:50 and "
+ "advertise vrf RED's routes in EVPN address family from "
+ "Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {"l2vpn": {"evpn": {"rd": "50.50.50.50:50"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("EVPN route for vrf RED has RD value as 50.50.50.50:50")
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="50.50.50.50:50"
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Configure RD for vrf RED manually as 100.100.100.100:100 and "
+ "advertise vrf RED's routes in EVPN address family from Edge-1 "
+ "router."
+ )
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}}
+ }
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for vrf RED is overridden with RD value as " "100.100.100.100:100."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="100.100.100.100:100"
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Configure RD for vrf BLUE manually same as vrf RED "
+ "(100.100.100.100:100) and advertise vrf RED and BLUE's routes "
+ "in EVPN address family from Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Delete manually configured RD and advertise vrf RED's routes "
+ "in EVPN address family from Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {"evpn": {"no rd": "100.100.100.100:100"}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure same RD value for vrf GREEN, as auto generated RD "
+ "value for vrf RED on Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"rd": "10.0.0.33:1"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete auto configured RD value from vrf RED in EVPN " "address family.")
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"no rd": "10.0.0.33:1"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure RD value as 100.100.100:100")
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"rd": "100.100.100:100"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_RT_verification_manual_p0(request):
+ """
+ RT verification(manual)
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure RT for vrf RED manually as export 100:100 "
+ "and advertise vrf RED's routes in EVPN address family"
+ " from Edge-1 router."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}}
+ },
+ "ipv6": {
+ "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}}
+ },
+ "l2vpn": {
+ "evpn": {"route-target": {"export": [{"value": "100:100"}]}}
+ },
+ }
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32"
+ " and 10::1/128 have RT value as 100:100."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt="100:100"
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Configure RT for vrf RED manually as export 500:500 and"
+ " advertise vrf RED's routes in EVPN address family from"
+ " e1 router."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {"route-target": {"export": [{"value": "500:500"}]}}
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32"
+ " and 10::1/128 have RT value as 500:500."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt=["100:100", "500:500"]
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Import RT value 100:100 and 500:500 in vrf BLUE manually on"
+ " peer router DCG-1 and DCG-2."
+ )
+
+ input_dict_rt = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [
+ {"value": "100:100"},
+ {"value": "500:500"},
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [
+ {"value": "100:100"},
+ {"value": "500:500"},
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for 10.1.1.1/32 and 10::1 should be installed "
+ "in vrf BLUE on DCG-1 and DCG-2 and further advertised to "
+ "VNF router."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "Delete import RT value 500:500 in vrf BLUE manually on "
+ "peer router DCG-1 and DCG-2."
+ )
+
+ input_dict_rt = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [{"value": "500:500", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [{"value": "500:500", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt=["100:100", "500:500"]
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step("Delete RT export value 100:100 for vrf RED on Edge-1")
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "export": [{"value": "100:100", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for 10.1.1.1/32 and 10::1 should be withdrawn "
+ "from vrf BLUE on DCG-1,DCG-2 and VNF router."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "d1", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: Routes are still "
+ "present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: Routes are still "
+ "present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Configure RT value as 100:100000010000010000101010 to check "
+ "the boundary value."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "export": [
+ {"value": "100:100000010000010000101010"}
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "CLI error: RT value: 100:100000010000010000101010 should not " "be configured"
+ )
+
+ dut = "e1"
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt="100:100000010000010000101010", expected=False
+ )
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: RT value of out"
+ " of boundary \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_active_standby_evpn_implementation_p1(request):
+ """
+ In an active/standby EVPN implementation, if active DCG goes down,
+ secondary takes over.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Taken care in base config: Configure BGP neighborship for both "
+ "address families(IPv4 & IPv6) between DCG-1/DCG-2 and VFN routers"
+ "(R3 and R4)."
+ )
+
+ step(
+ "BGP neighborships come up within defined VRFs. Please use below "
+ "command: sh bgp vrf all summary"
+ )
+
+ result = verify_bgp_convergence(tgen, topo, "d1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_bgp_convergence(tgen, topo, "d2")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Advertise prefixes from VNF routers R3 and R4 in associated "
+ "VRFs for both address-families."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Redistribute static in (IPv4 and IPv6) address-family "
+ "on Edge-1 for all VRFs."
+ )
+
+ input_dict_2 = {}
+ for dut in ["r3", "r4"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ if dut == "r3":
+ VRFS = ["RED"]
+ AS_NUM = [3]
+ if dut == "r4":
+ VRFS = ["BLUE", "GREEN"]
+ AS_NUM = [4, 4]
+
+ for vrf, as_num in zip(VRFS, AS_NUM):
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Prefixes are received in respective VRFs on DCG-1/DCG-2.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Taken care in base config: Advertise VRF routes in EVPN "
+ "address-family from DCG-1 and DCG-2 router."
+ )
+
+ step("Verify on Edge-1 that EVPN routes are installed via next-hop " "as DCG-2.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ if addr_type == "ipv4":
+ result = verify_rib(
+ tgen, addr_type, "e1", input_routes, next_hop=BRIDGE_INTF2
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure 'next-hop self' on DCG-1 for peer Edge-1 in EVPN " "address-family."
+ )
+
+ input_dict_3 = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4": {"d1-link1": {"next_hop_self": True}}
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ logger.info(
+ "Creating route-map so ipv6 glpbal ip wpuld be preferred " "as next-hop"
+ )
+
+ step(
+ "Verify on Edge-1 that EVPN routes are now preferred via "
+ "next-hop as DCG-1(iBGP) due to shortest AS-Path."
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ logger.info("Verifying only ipv4 routes")
+ if addr_type != "ipv4":
+ continue
+
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ next_hop = topo["routers"]["d1"]["links"]["e1-link1"]["ipv4"].split("/")[0]
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes, next_hop=next_hop)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_routes_from_VNFs_p1(request):
+ """
+ EVPN routes are advertised/withdrawn, based on VNFs
+ advertising/withdrawing IP prefixes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Taken care in base config: Advertise VNFs'(R1 and R2) "
+ "originated routes in EVPN address-family from Edge-1 to "
+ "DCG-1 and DCG-2 routers."
+ )
+ step(
+ "Taken care in base config: Advertise IPv4 and IPv6 routes "
+ "from default vrf in EVPN address-family from Edge-1."
+ )
+
+ step(
+ "Verify on DCG-2 that VNF routes are received in respective "
+ "VRFs along with auto derived RD/RT values 'show bgp l2vpn evpn'"
+ )
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_evpn_routes(tgen, topo, dut, input_routes)
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_evpn_routes(tgen, topo, dut, input_routes)
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Verify on R3 and R4 that DCG-2 further advertises all EVPN "
+ "routes to corresponding VRFs."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 receives EVPN routes associated to default "
+ "VRF and install in default IP routing table as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Withdraw the IP prefixes from VFN(R1).")
+ dut = "r1"
+ input_dict_2 = {}
+ static_routes = topo["routers"][dut]["static_routes"]
+ for static_route in static_routes:
+ static_route["delete"] = True
+ temp = {dut: {"static_routes": [static_route]}}
+ input_dict_2.update(temp)
+
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 removes EVPN routes corresponding to vrf RED and "
+ "send an withdraw to VNF(R3) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Re-advertise IP prefixes from VFN(R1).")
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 receives EVPN routes corresponding to vrf RED "
+ "again and send an update to VNF(R3) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete vrf BLUE from router Edge-1")
+ input_dict_3 = {"e1": {"vrfs": [{"name": "BLUE", "id": "2", "delete": True}]}}
+
+ result = create_vrf_cfg(tgen, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that DCG-2 removes EVPN routes corresponding to "
+ "vrf BLUE and send an withdraw to VNF(R4) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Add vrf BLUE on router Edge-1 again.")
+ interface = topo["routers"]["e1"]["links"]["r2-link1"]["interface"]
+ input_dict_3 = {
+ "e1": {
+ "links": {
+ "r2-link1": {
+ "interface": interface,
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "BLUE",
+ }
+ },
+ "vrfs": [{"name": "BLUE", "id": "2"}],
+ }
+ }
+ result = create_vrf_cfg(tgen, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ logger.info(
+ "After deleting VRFs ipv6 addresses wil be deleted "
+ "from kernel Adding back ipv6 addresses"
+ )
+ dut = "e1"
+ vrfs = ["BLUE"]
+
+ for vrf in vrfs:
+ for c_link, c_data in topo["routers"][dut]["links"].items():
+ if "vrf" in c_data:
+ if c_data["vrf"] != vrf:
+ continue
+
+ intf_name = c_data["interface"]
+ intf_ipv6 = c_data["ipv6"]
+
+ create_interface_in_kernel(
+ tgen, dut, intf_name, intf_ipv6, vrf, create=False
+ )
+
+ logger.info("Wait for 60 sec.")
+ sleep(60)
+
+ step(
+ "Verify that DCG-2 receives EVPN routes corresponding to "
+ "vrf BLUE again and send an update to VNF(R4) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Withdraw IPv6 address-family in EVPN advertisements for " "VRF GREEN")
+ addr_type = "ipv6"
+ input_dict_4 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "advertise": {addr_type: {"unicast": {"delete": True}}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that EVPN routes (IPv6)associated with vrf GREEN are "
+ "withdrawn from DCG-2 and VNF R4."
+ )
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Advertise IPv6 address-family in EVPN advertisements " "for VRF GREEN.")
+ addr_type = "ipv6"
+ input_dict_4 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "l2vpn": {"evpn": {"advertise": {addr_type: {"unicast": {}}}}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {
+ "static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize(
+ "attribute", [{"route-type": "prefix"}, {"vni": VNI_1}, {"rt": "300:300"}]
+)
+def test_route_map_operations_for_evpn_address_family_p1(request, attribute):
+ """
+ Route-map operations for EVPN address family.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise VRF routes in EVPN address family from Edge-1 router."
+ " Configure a route-map on e1 to filter EVPN routes based on"
+ " below keywords: route-type: prefix"
+ )
+
+ for key, value in attribute.items():
+ if key == "rt":
+ logger.info("Creating extcommunity using raw_config")
+ raw_config = {
+ "d2": {
+ "raw_config": [
+ "bgp extcommunity-list standard ECOMM300 permit {} {}".format(
+ key, value
+ )
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "set": {"extcommunity": {key: value}}}
+ ]
+ }
+ },
+ "d2": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"extcommunity": "ECOMM300"}}
+ ]
+ }
+ },
+ }
+
+ else:
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"evpn": {key: value}}}
+ ]
+ }
+ },
+ "d2": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"evpn": {key: value}}}
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d2": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_route_type",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4": {
+ "d2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_route_type",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router DCG-2 that EVPN routes corresponding to all "
+ "VRFs are received. As all EVPN routes are type-5 only."
+ )
+
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_evpn_routes(tgen, topo, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_evpn_routes(tgen, topo, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize("attribute", ["locPrf", "weight", "path"])
+def test_bgp_attributes_for_evpn_address_family_p1(request, attribute):
+ """
+ BGP attributes for EVPN address-family.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ topo_local = deepcopy(topo)
+
+ logger.info("Modifying topology b/w e1 and d1 from iBGP to eBGP")
+ step("Delete BGP config for vrf RED.")
+
+ if attribute == "locPrf":
+ input_dict_vni = {
+ "d1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {}
+ for dut in ["d1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ INDEX = [0, 1, 2, 3]
+ VRFS = ["RED", "BLUE", "GREEN", None]
+ AS_NUM = [100, 100, 100, 100]
+
+ for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM):
+ topo_local["routers"][dut]["bgp"][index]["local_as"] = 200
+ if vrf:
+ temp[dut]["bgp"].append(
+ {"local_as": as_num, "vrf": vrf, "delete": True}
+ )
+ else:
+ temp[dut]["bgp"].append({"local_as": as_num, "delete": True})
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} on d1 :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = create_router_bgp(tgen, topo_local["routers"])
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Advertise VRF routes in EVPN address-family from DCG-1 " "and DCG-2 routers.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Redistribute static in (IPv4 and IPv6) address-family "
+ "on Edge-1 for all VRFs."
+ )
+
+ input_dict_2 = {}
+ for dut in ["r3", "r4"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ if dut == "r3":
+ VRFS = ["RED"]
+ AS_NUM = [3]
+ if dut == "r4":
+ VRFS = ["BLUE", "GREEN"]
+ AS_NUM = [4, 4]
+
+ for vrf, as_num in zip(VRFS, AS_NUM):
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router Edge-1 that EVPN routes corresponding to "
+ "all VRFs are received from both routers DCG-1 and DCG-2"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a route-map on Edge-1 to modify below BGP attributes "
+ "for EVPN address-family:"
+ )
+
+ if attribute == "path":
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_d1".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ attribute: {
+ "as_num": "123 231 321",
+ "as_action": "prepend",
+ }
+ },
+ }
+ ],
+ "rmap_d2".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ attribute: {"as_num": "121", "as_action": "prepend"}
+ },
+ }
+ ],
+ }
+ }
+ }
+ else:
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_d1".format(addr_type): [
+ {"action": "permit", "set": {attribute: 120}}
+ ],
+ "rmap_d2".format(addr_type): [
+ {"action": "permit", "set": {attribute: 150}}
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_d1",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "d2": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_d2",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router Edge-1 that EVPN routes are preferred via"
+ " DCG-1 or DCG-2 based on best path selection criteria "
+ "(according to the configured BGP attribute values in route-map)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, "e1", input_routes, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/isis-sr-topo1/rt1/isisd.conf b/tests/topotests/isis-sr-topo1/rt1/isisd.conf
index 70ae1b07f5..26ec4eb261 100644
--- a/tests/topotests/isis-sr-topo1/rt1/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt1/isisd.conf
@@ -21,6 +21,7 @@ interface eth-sw1
router isis 1
net 49.0000.0000.0000.0001.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/isis-sr-topo1/rt2/isisd.conf b/tests/topotests/isis-sr-topo1/rt2/isisd.conf
index 733f26bc62..8704a28b6c 100644
--- a/tests/topotests/isis-sr-topo1/rt2/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt2/isisd.conf
@@ -32,6 +32,7 @@ interface eth-rt4-2
router isis 1
net 49.0000.0000.0000.0002.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/isis-sr-topo1/rt3/isisd.conf b/tests/topotests/isis-sr-topo1/rt3/isisd.conf
index ceb982ca32..5a0add22a9 100644
--- a/tests/topotests/isis-sr-topo1/rt3/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt3/isisd.conf
@@ -32,6 +32,7 @@ interface eth-rt5-2
router isis 1
net 49.0000.0000.0000.0003.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 17000 24999
diff --git a/tests/topotests/isis-sr-topo1/rt4/isisd.conf b/tests/topotests/isis-sr-topo1/rt4/isisd.conf
index 07a7867cbb..39003b9d7b 100644
--- a/tests/topotests/isis-sr-topo1/rt4/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt4/isisd.conf
@@ -39,6 +39,7 @@ interface eth-rt6
router isis 1
net 49.0000.0000.0000.0004.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/isis-sr-topo1/rt5/isisd.conf b/tests/topotests/isis-sr-topo1/rt5/isisd.conf
index b0fcdede07..e693ca156c 100644
--- a/tests/topotests/isis-sr-topo1/rt5/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt5/isisd.conf
@@ -39,6 +39,7 @@ interface eth-rt6
router isis 1
net 49.0000.0000.0000.0005.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/isis-sr-topo1/rt6/isisd.conf b/tests/topotests/isis-sr-topo1/rt6/isisd.conf
index 16c34dfae6..3b85dbae4e 100644
--- a/tests/topotests/isis-sr-topo1/rt6/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt6/isisd.conf
@@ -27,6 +27,7 @@ interface eth-rt5
router isis 1
net 49.0000.0000.0000.0006.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
index 600d640a70..a1662dc411 100755
--- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
+++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
@@ -154,7 +154,7 @@ def teardown_module(mod):
tgen.stop_topology()
-def router_compare_json_output(rname, command, reference):
+def router_compare_json_output(rname, command, reference, count=80, wait=1):
"Compare router JSON output"
logger.info('Comparing router "%s" "%s" output', rname, command)
@@ -163,9 +163,9 @@ def router_compare_json_output(rname, command, reference):
filename = "{}/{}/{}".format(CWD, rname, reference)
expected = json.loads(open(filename).read())
- # Run test function until we get an result. Wait at most 80 seconds.
+ # Run test function until we get an result.
test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
- _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ _, diff = topotest.run_and_expect(test_func, None, count, wait)
assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
assert diff is None, assertmsg
@@ -279,10 +279,12 @@ def test_ldp_pseudowires_after_link_down():
tgen.gears["r1"].peer_link_enable("r1-eth1", False)
topotest.sleep(5, "Waiting for the network to reconverge")
- # check if the pseudowire is still up (using an alternate path for nexthop resolution)
+ # check if the pseudowire is still up (using an alternate path
+ # for nexthop resolution). Give some extra wait time.
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
- rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
+ rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref",
+ count=160, wait=1
)
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index 971bbd0f3b..7b1eead944 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -27,6 +27,8 @@ import sys
from lib import topotest
from lib.topolog import logger
+from lib.topogen import TopoRouter, get_topogen
+
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
@@ -166,6 +168,7 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
ipv4_data = bgp_addr_data.setdefault("ipv4", {})
ipv6_data = bgp_addr_data.setdefault("ipv6", {})
+ l2vpn_data = bgp_addr_data.setdefault("l2vpn", {})
neigh_unicast = (
True
@@ -174,6 +177,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
else False
)
+ l2vpn_evpn = True if l2vpn_data.setdefault("evpn", {}) else False
+
if neigh_unicast:
data_all_bgp = __create_bgp_unicast_neighbor(
tgen,
@@ -184,6 +189,11 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
config_data=data_all_bgp,
)
+ if l2vpn_evpn:
+ data_all_bgp = __create_l2vpn_evpn_address_family(
+ tgen, topo, bgp_data, router, config_data=data_all_bgp
+ )
+
try:
result = create_common_configuration(
tgen, router, data_all_bgp, "bgp", build, load_config
@@ -467,6 +477,166 @@ def __create_bgp_unicast_neighbor(
return config_data
+def __create_l2vpn_evpn_address_family(
+ tgen, topo, input_dict, router, config_data=None
+):
+ """
+ Helper API to create configuration for l2vpn evpn address-family
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring
+ from testcase
+ * `router` : router id to be configured.
+ * `build` : Only for initial setup phase this is set as True.
+ """
+
+ result = False
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ bgp_data = input_dict["address_family"]
+
+ for family_type, family_dict in bgp_data.iteritems():
+ if family_type != "l2vpn":
+ continue
+
+ family_data = family_dict["evpn"]
+ if family_data:
+ config_data.append("address-family l2vpn evpn")
+
+ advertise_data = family_data.setdefault("advertise", {})
+ neighbor_data = family_data.setdefault("neighbor", {})
+ advertise_all_vni_data = family_data.setdefault("advertise-all-vni", None)
+ rd_data = family_data.setdefault("rd", None)
+ no_rd_data = family_data.setdefault("no rd", False)
+ route_target_data = family_data.setdefault("route-target", {})
+
+ if advertise_data:
+ for address_type, unicast_type in advertise_data.items():
+
+ if isinstance(unicast_type, dict):
+ for key, value in unicast_type.items():
+ cmd = "advertise {} {}".format(address_type, key)
+
+ if value:
+ route_map = value.setdefault("route-map", {})
+ advertise_del_action = value.setdefault("delete", None)
+
+ if route_map:
+ cmd = "{} route-map {}".format(cmd, route_map)
+
+ if advertise_del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ if neighbor_data:
+ for neighbor, neighbor_data in neighbor_data.items():
+ ipv4_neighbor = neighbor_data.setdefault("ipv4", {})
+ ipv6_neighbor = neighbor_data.setdefault("ipv6", {})
+
+ if ipv4_neighbor:
+ for neighbor_name, action in ipv4_neighbor.items():
+ neighbor_ip = topo[neighbor]["links"][neighbor_name][
+ "ipv4"
+ ].split("/")[0]
+
+ if isinstance(action, dict):
+ next_hop_self = action.setdefault("next_hop_self", None)
+ route_maps = action.setdefault("route_maps", {})
+
+ if next_hop_self is not None:
+ if next_hop_self is True:
+ config_data.append(
+ "neighbor {} "
+ "next-hop-self".format(neighbor_ip)
+ )
+ elif next_hop_self is False:
+ config_data.append(
+ "no neighbor {} "
+ "next-hop-self".format(neighbor_ip)
+ )
+
+ if route_maps:
+ for route_map in route_maps:
+ name = route_map.setdefault("name", {})
+ direction = route_map.setdefault("direction", "in")
+ del_action = route_map.setdefault("delete", False)
+
+ if not name:
+ logger.info(
+ "Router %s: 'name' "
+ "not present in "
+ "input_dict for BGP "
+ "neighbor route name",
+ router,
+ )
+ else:
+ cmd = "neighbor {} route-map {} " "{}".format(
+ neighbor_ip, name, direction
+ )
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ else:
+ if action == "activate":
+ cmd = "neighbor {} activate".format(neighbor_ip)
+ elif action == "deactivate":
+ cmd = "no neighbor {} activate".format(neighbor_ip)
+
+ config_data.append(cmd)
+
+ if ipv6_neighbor:
+ for neighbor_name, action in ipv4_neighbor.items():
+ neighbor_ip = topo[neighbor]["links"][neighbor_name][
+ "ipv6"
+ ].split("/")[0]
+ if action == "activate":
+ cmd = "neighbor {} activate".format(neighbor_ip)
+ elif action == "deactivate":
+ cmd = "no neighbor {} activate".format(neighbor_ip)
+
+ config_data.append(cmd)
+
+ if advertise_all_vni_data == True:
+ cmd = "advertise-all-vni"
+ config_data.append(cmd)
+ elif advertise_all_vni_data == False:
+ cmd = "no advertise-all-vni"
+ config_data.append(cmd)
+
+ if rd_data:
+ cmd = "rd {}".format(rd_data)
+ config_data.append(cmd)
+
+ if no_rd_data:
+ cmd = "no rd {}".format(no_rd_data)
+ config_data.append(cmd)
+
+ if route_target_data:
+ for rt_type, rt_dict in route_target_data.items():
+ for _rt_dict in rt_dict:
+ rt_value = _rt_dict.setdefault("value", None)
+ del_rt = _rt_dict.setdefault("delete", None)
+
+ if rt_value:
+ cmd = "route-target {} {}".format(rt_type, rt_value)
+ if del_rt:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return config_data
+
+
def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
"""
Helper API to create neighbor specific configuration
@@ -489,7 +659,7 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
for dest_link, peer in peer_dict["dest_link"].iteritems():
nh_details = topo[name]
- if "vrfs" in topo[router]:
+ if "vrfs" in topo[router] or type(nh_details["bgp"]) is list:
remote_as = nh_details["bgp"][0]["local_as"]
else:
remote_as = nh_details["bgp"]["local_as"]
@@ -887,19 +1057,16 @@ def verify_bgp_convergence(tgen, topo, dut=None):
API will verify if BGP is converged with in the given time frame.
Running "show bgp summary json" command and verify bgp neighbor
state is established,
-
Parameters
----------
* `tgen`: topogen object
* `topo`: input json file data
* `dut`: device under test
-
Usage
-----
# To veriry is BGP is converged for all the routers used in
topology
results = verify_bgp_convergence(tgen, topo, dut="r1")
-
Returns
-------
errormsg(str) or True
@@ -3463,3 +3630,552 @@ def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut):
return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2)
+def verify_attributes_for_evpn_routes(
+ tgen,
+ topo,
+ dut,
+ input_dict,
+ rd=None,
+ rt=None,
+ ethTag=None,
+ ipLen=None,
+ rd_peer=None,
+ rt_peer=None,
+):
+ """
+ API to verify rd and rt value using "sh bgp l2vpn evpn 10.1.1.1"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `input_dict`: having details like - for which route, rd value
+ needs to be verified
+ * `rd` : route distinguisher
+ * `rt` : route target
+ * `ethTag` : Ethernet Tag
+ * `ipLen` : IP prefix length
+ * `rd_peer` : Peer name from which RD will be auto-generated
+ * `rt_peer` : Peer name from which RT will be auto-generated
+
+ Usage
+ -----
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [{
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED"
+ }]
+ }
+ }
+
+ result = verify_attributes_for_evpn_routes(tgen, topo,
+ input_dict, rd = "10.0.0.33:1")
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for router in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "static_routes" in input_dict[router]:
+ for static_route in input_dict[router]["static_routes"]:
+ network = static_route["network"]
+
+ if "vrf" in static_route:
+ vrf = static_route["vrf"]
+
+ if type(network) is not list:
+ network = [network]
+
+ for route in network:
+ route = route.split("/")[0]
+ _addr_type = validate_ip_address(route)
+ if "v4" in _addr_type:
+ input_afi = "v4"
+ elif "v6" in _addr_type:
+ input_afi = "v6"
+
+ cmd = "show bgp l2vpn evpn {} json".format(route)
+ evpn_rd_value_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(evpn_rd_value_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if rd is not None and rd != "auto":
+ logger.info(
+ "[DUT: %s]: Verifying rd value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rd in evpn_rd_value_json:
+ rd_value_json = evpn_rd_value_json[rd]
+ if rd_value_json["rd"] != rd:
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " RD value for EVPN route: %s"
+ "[FAILED]!!, EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rd, rd_value_json["rd"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RD value for"
+ " EVPN route: %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rd,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD : %s is not present"
+ " in cli json output" % (dut, rd)
+ )
+ return errormsg
+
+ if rd == "auto":
+ logger.info(
+ "[DUT: %s]: Verifying auto-rd value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rd_peer:
+ index = 1
+ vni_dict = {}
+
+ rnode = tgen.routers()[rd_peer]
+ vrfs = topo["routers"][rd_peer]["vrfs"]
+ for vrf_dict in vrfs:
+ vni_dict[vrf_dict["name"]] = index
+ index += 1
+
+ show_bgp_json = run_frr_cmd(
+ rnode, "show bgp vrf all summary json", isjson=True
+ )
+
+ # Verifying output dictionary show_bgp_json is empty
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ show_bgp_json_vrf = show_bgp_json[vrf]
+ for afi, afi_data in show_bgp_json_vrf.items():
+ if input_afi not in afi:
+ continue
+ router_id = afi_data["routerId"]
+
+ rd = "{}:{}".format(router_id, vni_dict[vrf])
+ if rd in evpn_rd_value_json:
+ rd_value_json = evpn_rd_value_json[rd]
+ if rd_value_json["rd"] != rd:
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " RD value for EVPN route: %s"
+ "[FAILED]!!, EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rd, rd_value_json["rd"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RD value for"
+ " EVPN route: %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rd,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD : %s is not present"
+ " in cli json output" % (dut, rd)
+ )
+ return errormsg
+
+ if rt == "auto":
+ logger.info(
+ "[DUT: %s]: Verifying auto-rt value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rt_peer:
+ vni_dict = {}
+
+ rnode = tgen.routers()[rt_peer]
+ show_bgp_json = run_frr_cmd(
+ rnode, "show bgp vrf all summary json", isjson=True
+ )
+
+ # Verifying output dictionary show_bgp_json is empty
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ show_bgp_json_vrf = show_bgp_json[vrf]
+ for afi, afi_data in show_bgp_json_vrf.items():
+ if input_afi not in afi:
+ continue
+ as_num = afi_data["as"]
+
+ show_vrf_vni_json = run_frr_cmd(
+ rnode, "show vrf vni json", isjson=True
+ )
+
+ vrfs = show_vrf_vni_json["vrfs"]
+ for vrf_dict in vrfs:
+ if vrf_dict["vrf"] == vrf:
+ vni_dict[vrf_dict["vrf"]] = str(vrf_dict["vni"])
+
+ # If AS is 4 byte, FRR uses only the lower 2 bytes of ASN+VNI
+ # for auto derived RT value.
+ if as_num > 65535:
+ as_bin = bin(as_num)
+ as_bin = as_bin[-16:]
+ as_num = int(as_bin, 2)
+
+ rt = "{}:{}".format(str(as_num), vni_dict[vrf])
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ for rt_data in route_data["paths"]:
+ if vni_dict[vrf] == rt_data["VNI"]:
+ rt_string = rt_data["extendedCommunity"][
+ "string"
+ ]
+ rt_input = "RT:{}".format(rt)
+ if rt_input not in rt_string:
+ errormsg = (
+ "[DUT: %s] Failed:"
+ " Verifying RT "
+ "value for EVPN "
+ " route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rt_input, rt_string)
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying "
+ "RT value for EVPN "
+ "route: %s [PASSED]||"
+ "Found Exprected: %s",
+ dut,
+ route,
+ rt_input,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Route : %s is not"
+ " present in cli json output" % (dut, route)
+ )
+ return errormsg
+
+ if rt is not None and rt != "auto":
+ logger.info(
+ "[DUT: %s]: Verifying rt value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if type(rt) is not list:
+ rt = [rt]
+
+ for _rt in rt:
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ for rt_data in route_data["paths"]:
+ rt_string = rt_data["extendedCommunity"][
+ "string"
+ ]
+ rt_input = "RT:{}".format(_rt)
+ if rt_input not in rt_string:
+ errormsg = (
+ "[DUT: %s] Failed: "
+ "Verifying RT value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rt_input, rt_string)
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RT"
+ " value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rt_input,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Route : %s is not"
+ " present in cli json output" % (dut, route)
+ )
+ return errormsg
+
+ if ethTag is not None:
+ logger.info(
+ "[DUT: %s]: Verifying ethTag value for " "evpn route :", dut
+ )
+
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ if route_data["ethTag"] != ethTag:
+ errormsg = (
+ "[DUT: %s] RD: %s, Failed: "
+ "Verifying ethTag value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (
+ dut,
+ _rd,
+ route,
+ ethTag,
+ route_data["ethTag"],
+ )
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: RD: %s, Verifying "
+ "ethTag value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ _rd,
+ route,
+ ethTag,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD: %s, Route : %s "
+ "is not present in cli json "
+ "output" % (dut, _rd, route)
+ )
+ return errormsg
+
+ if ipLen is not None:
+ logger.info(
+ "[DUT: %s]: Verifying ipLen value for " "evpn route :", dut
+ )
+
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ if route_data["ipLen"] != int(ipLen):
+ errormsg = (
+ "[DUT: %s] RD: %s, Failed: "
+ "Verifying ipLen value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, _rd, route, ipLen, route_data["ipLen"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: RD: %s, Verifying "
+ "ipLen value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ _rd,
+ route,
+ ipLen,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD: %s, Route : %s "
+ "is not present in cli json "
+ "output " % (dut, route)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
+
+
+@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2)
+def verify_evpn_routes(
+ tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None
+):
+ """
+ API to verify evpn routes using "sh bgp l2vpn evpn"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `input_dict`: having details like - for which route, rd value
+ needs to be verified
+ * `route_type` : Route type 5 is supported as of now
+ * `EthTag` : Ethernet tag, by-default is 0
+ * `next_hop` : Prefered nexthop for the evpn routes
+
+ Usage
+ -----
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [{
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED"
+ }]
+ }
+ }
+ result = verify_evpn_routes(tgen, topo, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying evpn routes: ", dut)
+
+ if "static_routes" in input_dict[router]:
+ for static_route in input_dict[router]["static_routes"]:
+ network = static_route["network"]
+
+ if type(network) is not list:
+ network = [network]
+
+ missing_routes = {}
+ for route in network:
+ rd_keys = 0
+ ip_len = route.split("/")[1]
+ route = route.split("/")[0]
+
+ prefix = "[{}]:[{}]:[{}]:[{}]".format(
+ routeType, EthTag, ip_len, route
+ )
+
+ cmd = "show bgp l2vpn evpn route json"
+ evpn_value_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ if not bool(evpn_value_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if evpn_value_json["numPrefix"] == 0:
+ errormsg = "[DUT: %s]: No EVPN prefixes exist" % (dut)
+ return errormsg
+
+ for key, route_data_json in evpn_value_json.items():
+ if isinstance(route_data_json, dict):
+ rd_keys += 1
+ if prefix not in route_data_json:
+ missing_routes[key] = prefix
+
+ if rd_keys == len(missing_routes.keys()):
+ errormsg = (
+ "[DUT: %s]: "
+ "Missing EVPN routes: "
+ "%s [FAILED]!!" % (dut, list(set(missing_routes.values())))
+ )
+ return errormsg
+
+ for key, route_data_json in evpn_value_json.items():
+ if isinstance(route_data_json, dict):
+ if prefix not in route_data_json:
+ continue
+
+ for paths in route_data_json[prefix]["paths"]:
+ for path in paths:
+ if path["routeType"] != routeType:
+ errormsg = (
+ "[DUT: %s]: "
+ "Verifying routeType "
+ "for EVPN route: %s "
+ "[FAILED]!! "
+ "Expected: %s, "
+ "Found: %s"
+ % (
+ dut,
+ prefix,
+ routeType,
+ path["routeType"],
+ )
+ )
+ return errormsg
+
+ elif next_hop:
+ for nh_dict in path["nexthops"]:
+ if nh_dict["ip"] != next_hop:
+ errormsg = (
+ "[DUT: %s]: "
+ "Verifying "
+ "nexthop for "
+ "EVPN route: %s"
+ "[FAILED]!! "
+ "Expected: %s,"
+ " Found: %s"
+ % (
+ dut,
+ prefix,
+ next_hop,
+ nh_dict["ip"],
+ )
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying "
+ "EVPN route : %s, "
+ "routeType: %s is "
+ "installed "
+ "[PASSED]|| ",
+ dut,
+ prefix,
+ routeType,
+ )
+ return True
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return False
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index d72d0aa223..e4d72ea2d7 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -933,6 +933,16 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
)
rnode.run(cmd)
+ if vni:
+ config_data.append("vrf {}".format(vrf["name"]))
+ cmd = "vni {}".format(vni)
+ config_data.append(cmd)
+
+ if del_vni:
+ config_data.append("vrf {}".format(vrf["name"]))
+ cmd = "no vni {}".format(del_vni)
+ config_data.append(cmd)
+
result = create_common_configuration(
tgen, c_router, config_data, "vrf", build=build
)
@@ -984,6 +994,34 @@ def create_interface_in_kernel(
rnode.run(cmd)
+def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False):
+ """
+ Cretae interfaces in kernel for ipv4/ipv6
+ Config is done in Linux Kernel:
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut` : Device for which interfaces to be added
+ * `intf_name` : interface name
+ * `ifaceaction` : False to shutdown and True to bringup the
+ ineterface
+ """
+
+ rnode = tgen.routers()[dut]
+
+ cmd = "ip link set dev"
+ if ifaceaction:
+ action = "up"
+ cmd = "{} {} {}".format(cmd, intf_name, action)
+ else:
+ action = "down"
+ cmd = "{} {} {}".format(cmd, intf_name, action)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+
def validate_ip_address(ip_address):
"""
Validates the type of ip address
@@ -1042,7 +1080,7 @@ def check_address_types(addr_type=None):
return addr_types
if addr_type not in addr_types:
- logger.error(
+ logger.debug(
"{} not in supported/configured address types {}".format(
addr_type, addr_types
)
@@ -1732,6 +1770,7 @@ def create_route_maps(tgen, input_dict, build=False):
set_action = set_data.setdefault("set_action", None)
nexthop = set_data.setdefault("nexthop", None)
origin = set_data.setdefault("origin", None)
+ ext_comm_list = set_data.setdefault("extcommunity", {})
# Local Preference
if local_preference:
@@ -1796,6 +1835,19 @@ def create_route_maps(tgen, input_dict, build=False):
logger.error("In large_comm_list 'id' not" " provided")
return False
+ if ext_comm_list:
+ rt = ext_comm_list.setdefault("rt", None)
+ del_comm = ext_comm_list.setdefault("delete", None)
+ if rt:
+ cmd = "set extcommunity rt {}".format(rt)
+ if del_comm:
+ cmd = "{} delete".format(cmd)
+
+ rmap_data.append(cmd)
+ else:
+ logger.debug("In ext_comm_list 'rt' not" " provided")
+ return False
+
# Weight
if weight:
rmap_data.append("set weight {}".format(weight))
@@ -2151,6 +2203,243 @@ def addKernelRoute(
return True
+def configure_vxlan(tgen, input_dict):
+ """
+ Add and configure vxlan
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for vxlan config
+
+ Usage:
+ ------
+ input_dict= {
+ "dcg2":{
+ "vxlan":[{
+ "vxlan_name": "vxlan75100",
+ "vxlan_id": "75100",
+ "dstport": 4789,
+ "local_addr": "120.0.0.1",
+ "learning": "no",
+ "delete": True
+ }]
+ }
+ }
+
+ configure_vxlan(tgen, input_dict)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "vxlan" in input_dict[dut]:
+ for vxlan_dict in input_dict[dut]["vxlan"]:
+ cmd = "ip link "
+
+ del_vxlan = vxlan_dict.setdefault("delete", None)
+ vxlan_names = vxlan_dict.setdefault("vxlan_name", [])
+ vxlan_ids = vxlan_dict.setdefault("vxlan_id", [])
+ dstport = vxlan_dict.setdefault("dstport", None)
+ local_addr = vxlan_dict.setdefault("local_addr", None)
+ learning = vxlan_dict.setdefault("learning", None)
+
+ config_data = []
+ if vxlan_names and vxlan_ids:
+ for vxlan_name, vxlan_id in zip(vxlan_names, vxlan_ids):
+ cmd = "ip link"
+
+ if del_vxlan:
+ cmd = "{} del {} type vxlan id {}".format(
+ cmd, vxlan_name, vxlan_id
+ )
+ else:
+ cmd = "{} add {} type vxlan id {}".format(
+ cmd, vxlan_name, vxlan_id
+ )
+
+ if dstport:
+ cmd = "{} dstport {}".format(cmd, dstport)
+
+ if local_addr:
+ ip_cmd = "ip addr add {} dev {}".format(
+ local_addr, vxlan_name
+ )
+ if del_vxlan:
+ ip_cmd = "ip addr del {} dev {}".format(
+ local_addr, vxlan_name
+ )
+
+ config_data.append(ip_cmd)
+
+ cmd = "{} local {}".format(cmd, local_addr)
+
+ if learning == "no":
+ cmd = "{} nolearning".format(cmd)
+
+ elif learning == "yes":
+ cmd = "{} learning".format(cmd)
+
+ config_data.append(cmd)
+
+ try:
+ for _cmd in config_data:
+ logger.info("[DUT: %s]: Running command: %s", dut, _cmd)
+ rnode.run(_cmd)
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def configure_brctl(tgen, topo, input_dict):
+ """
+ Add and configure brctl
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for brctl config
+
+ Usage:
+ ------
+ input_dict= {
+ dut:{
+ "brctl": [{
+ "brctl_name": "br100",
+ "addvxlan": "vxlan75100",
+ "vrf": "RED",
+ "stp": "off"
+ }]
+ }
+ }
+
+ configure_brctl(tgen, topo, input_dict)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "brctl" in input_dict[dut]:
+ for brctl_dict in input_dict[dut]["brctl"]:
+
+ brctl_names = brctl_dict.setdefault("brctl_name", [])
+ addvxlans = brctl_dict.setdefault("addvxlan", [])
+ stp_values = brctl_dict.setdefault("stp", [])
+ vrfs = brctl_dict.setdefault("vrf", [])
+
+ ip_cmd = "ip link set"
+ for brctl_name, vxlan, vrf, stp in zip(
+ brctl_names, addvxlans, vrfs, stp_values
+ ):
+
+ ip_cmd_list = []
+ cmd = "ip link add name {} type bridge stp_state {}".format(brctl_name, stp)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ ip_cmd_list.append("{} up dev {}".format(ip_cmd, brctl_name))
+
+ if vxlan:
+ cmd = "{} dev {} master {}".format(ip_cmd, vxlan, brctl_name)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ ip_cmd_list.append("{} up dev {}".format(ip_cmd, vxlan))
+
+ if vrf:
+ ip_cmd_list.append(
+ "{} dev {} master {}".format(ip_cmd, brctl_name, vrf)
+ )
+
+ for intf_name, data in topo["routers"][dut]["links"].items():
+ if "vrf" not in data:
+ continue
+
+ if data["vrf"] == vrf:
+ ip_cmd_list.append(
+ "{} up dev {}".format(ip_cmd, data["interface"])
+ )
+
+ try:
+ for _ip_cmd in ip_cmd_list:
+ logger.info("[DUT: %s]: Running command: %s", dut, _ip_cmd)
+ rnode.run(_ip_cmd)
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def configure_interface_mac(tgen, input_dict):
+ """
+ Add and configure brctl
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for mac config
+
+ input_mac= {
+ "edge1":{
+ "br75100": "00:80:48:BA:d1:00,
+ "br75200": "00:80:48:BA:d1:00
+ }
+ }
+
+ configure_interface_mac(tgen, input_mac)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ for intf, mac in input_dict[dut].items():
+ cmd = "ifconfig {} hw ether {}".format(intf, mac)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+
+ try:
+ result = rnode.run(cmd)
+ if len(result) != 0:
+ return result
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ return True
+
+
#############################################
# Verification APIs
#############################################
@@ -2875,3 +3164,283 @@ def verify_create_community_list(tgen, input_dict):
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return True
+
+
+def verify_cli_json(tgen, input_dict):
+ """
+ API to verify if JSON is available for clis
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: CLIs for which JSON needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "cli": ["show evpn vni detail", show evpn rmac vni all]
+ }
+ }
+
+ result = verify_cli_json(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ for cli in input_dict[dut]["cli"]:
+ logger.info(
+ "[DUT: %s]: Verifying JSON is available for " "CLI %s :", dut, cli
+ )
+
+ test_cli = "{} json".format(cli)
+ ret_json = rnode.vtysh_cmd(test_cli, isjson=True)
+ if not bool(ret_json):
+ errormsg = "CLI: %s, JSON format is not available" % (cli)
+ return errormsg
+ elif "unknown" in ret_json or "Unknown" in ret_json:
+ errormsg = "CLI: %s, JSON format is not available" % (cli)
+ return errormsg
+ else:
+ logger.info(
+ "CLI : %s JSON format is available: " "\n %s", cli, ret_json
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2)
+def verify_evpn_vni(tgen, input_dict):
+ """
+ API to verify evpn vni details using "show evpn vni detail json"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: having details like - for which router, evpn details
+ needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "vni": [
+ {
+ "75100":{
+ "vrf": "RED",
+ "vxlanIntf": "vxlan75100",
+ "localVtepIp": "120.1.1.1",
+ "sviIntf": "br100"
+ }
+ }
+ ]
+ }
+ }
+
+ result = verify_evpn_vni(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying evpn vni details :", dut)
+
+ cmd = "show evpn vni detail json"
+ evpn_all_vni_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(evpn_all_vni_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if "vni" in input_dict[dut]:
+ for vni_dict in input_dict[dut]["vni"]:
+ found = False
+ vni = vni_dict["name"]
+ for evpn_vni_json in evpn_all_vni_json:
+ if "vni" in evpn_vni_json:
+ if evpn_vni_json["vni"] != int(vni):
+ continue
+
+ for attribute in vni_dict.keys():
+ if vni_dict[attribute] != evpn_vni_json[attribute]:
+ errormsg = (
+ "[DUT: %s] Verifying "
+ "%s for VNI: %s [FAILED]||"
+ ", EXPECTED : %s "
+ " FOUND : %s"
+ % (
+ dut,
+ attribute,
+ vni,
+ vni_dict[attribute],
+ evpn_vni_json[attribute],
+ )
+ )
+ return errormsg
+
+ else:
+ found = True
+ logger.info(
+ "[DUT: %s] Verifying"
+ " %s for VNI: %s , "
+ "Found Expected : %s ",
+ dut,
+ attribute,
+ vni,
+ evpn_vni_json[attribute],
+ )
+
+ if evpn_vni_json["state"] != "Up":
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " State for VNI: %s is not Up" % (dut, vni)
+ )
+ return errormsg
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:"
+ " VNI: %s is not present in JSON" % (dut, vni)
+ )
+ return errormsg
+
+ if found:
+ logger.info(
+ "[DUT %s]: Verifying VNI : %s "
+ "details and state is Up [PASSED]!!",
+ dut,
+ vni,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:" " vni details are not present in input data" % (dut)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
+
+
+@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2)
+def verify_vrf_vni(tgen, input_dict):
+ """
+ API to verify vrf vni details using "show vrf vni json"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: having details like - for which router, evpn details
+ needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "vrfs": [
+ {
+ "RED":{
+ "vni": 75000,
+ "vxlanIntf": "vxlan75100",
+ "sviIntf": "br100",
+ "routerMac": "00:80:48:ba:d1:00",
+ "state": "Up"
+ }
+ }
+ ]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying vrf vni details :", dut)
+
+ cmd = "show vrf vni json"
+ vrf_all_vni_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(vrf_all_vni_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if "vrfs" in input_dict[dut]:
+ for vrfs in input_dict[dut]["vrfs"]:
+ for vrf, vrf_dict in vrfs.items():
+ found = False
+ for vrf_vni_json in vrf_all_vni_json["vrfs"]:
+ if "vrf" in vrf_vni_json:
+ if vrf_vni_json["vrf"] != vrf:
+ continue
+
+ for attribute in vrf_dict.keys():
+ if vrf_dict[attribute] == vrf_vni_json[attribute]:
+ found = True
+ logger.info(
+ "[DUT %s]: VRF: %s, "
+ "verifying %s "
+ ", Found Expected: %s "
+ "[PASSED]!!",
+ dut,
+ vrf,
+ attribute,
+ vrf_vni_json[attribute],
+ )
+ else:
+ errormsg = (
+ "[DUT: %s] VRF: %s, "
+ "verifying %s [FAILED!!] "
+ ", EXPECTED : %s "
+ ", FOUND : %s"
+ % (
+ dut,
+ vrf,
+ attribute,
+ vrf_dict[attribute],
+ vrf_vni_json[attribute],
+ )
+ )
+ return errormsg
+
+ else:
+ errormsg = "[DUT: %s] VRF: %s " "is not present in JSON" % (
+ dut,
+ vrf,
+ )
+ return errormsg
+
+ if found:
+ logger.info(
+ "[DUT %s] Verifying VRF: %s " " details [PASSED]!!",
+ dut,
+ vrf,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:" " vrf details are not present in input data" % (dut)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index efd5b90685..37b9715010 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -819,7 +819,9 @@ class TopoRouter(TopoGear):
if memleak_file is None:
return
- self.stop()
+ self.stop(False, False)
+ self.stop(wait=True)
+
self.logger.info("running memory leak report")
self.tgen.net[self.name].report_memory_leaks(memleak_file, testname)
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index bffb8208e7..b5fa2ea59b 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -35,6 +35,7 @@ import tempfile
import platform
import difflib
import time
+import signal
from lib.topolog import logger
from copy import deepcopy
@@ -51,6 +52,35 @@ from mininet.log import setLogLevel, info
from mininet.cli import CLI
from mininet.link import Intf
+def gdb_core(obj, daemon, corefiles):
+ gdbcmds = '''
+ info threads
+ bt full
+ disassemble
+ up
+ disassemble
+ up
+ disassemble
+ up
+ disassemble
+ up
+ disassemble
+ up
+ disassemble
+ '''
+ gdbcmds = [['-ex', i.strip()] for i in gdbcmds.strip().split('\n')]
+ gdbcmds = [item for sl in gdbcmds for item in sl]
+
+ daemon_path = os.path.join(obj.daemondir, daemon)
+ backtrace = subprocess.check_output(
+ ['gdb', daemon_path, corefiles[0], '--batch'] + gdbcmds
+ )
+ sys.stderr.write(
+ "\n%s: %s crashed. Core file found - Backtrace follows:\n"
+ % (obj.name, daemon)
+ )
+ sys.stderr.write("%s" % backtrace)
+ return backtrace
class json_cmp_result(object):
"json_cmp result class for better assertion messages"
@@ -422,6 +452,10 @@ def pid_exists(pid):
if pid <= 0:
return False
try:
+ os.waitpid(pid, os.WNOHANG)
+ except:
+ pass
+ try:
os.kill(pid, 0)
except OSError as err:
if err.errno == errno.ESRCH:
@@ -992,8 +1026,8 @@ class Router(Node):
os.system("chmod -R go+rw /tmp/topotests")
# Return count of running daemons
- def countDaemons(self):
- numRunning = 0
+ def listDaemons(self):
+ ret = []
rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
errors = ""
if re.search(r"No such file or directory", rundaemons):
@@ -1002,12 +1036,11 @@ class Router(Node):
for d in StringIO.StringIO(rundaemons):
daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
if daemonpid.isdigit() and pid_exists(int(daemonpid)):
- numRunning += 1
- return numRunning
+ ret.append(os.path.basename(d.rstrip().rsplit(".", 1)[0]))
+ return ret
def stopRouter(self, wait=True, assertOnError=True, minErrorVersion="5.1"):
# Stop Running FRR Daemons
- numRunning = 0
rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
errors = ""
if re.search(r"No such file or directory", rundaemons):
@@ -1016,24 +1049,36 @@ class Router(Node):
for d in StringIO.StringIO(rundaemons):
daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
if daemonpid.isdigit() and pid_exists(int(daemonpid)):
+ daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0])
logger.info(
"{}: stopping {}".format(
- self.name, os.path.basename(d.rstrip().rsplit(".", 1)[0])
+ self.name, daemonname
)
)
- self.cmd("kill -TERM %s" % daemonpid)
- self.waitOutput()
- if pid_exists(int(daemonpid)):
- numRunning += 1
-
- if wait and numRunning > 0:
- counter = 5
- while counter > 0 and numRunning > 0:
- sleep(2, "{}: waiting for daemons stopping".format(self.name))
- numRunning = self.countDaemons()
+ try:
+ os.kill(int(daemonpid), signal.SIGTERM)
+ except OSError as err:
+ if err.errno == errno.ESRCH:
+ logger.error("{}: {} left a dead pidfile (pid={})".format(self.name, daemonname, daemonpid))
+ else:
+ logger.info("{}: {} could not kill pid {}: {}".format(self.name, daemonname, daemonpid, str(err)))
+
+ if not wait:
+ return errors
+
+ running = self.listDaemons()
+
+ if running:
+ sleep(0.1, "{}: waiting for daemons stopping: {}".format(self.name, ', '.join(running)))
+ running = self.listDaemons()
+
+ counter = 20
+ while counter > 0 and running:
+ sleep(0.5, "{}: waiting for daemons stopping: {}".format(self.name, ', '.join(running)))
+ running = self.listDaemons()
counter -= 1
- if wait and numRunning > 0:
+ if running:
# 2nd round of kill if daemons didn't exit
for d in StringIO.StringIO(rundaemons):
daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
@@ -1048,13 +1093,15 @@ class Router(Node):
self.waitOutput()
self.cmd("rm -- {}".format(d.rstrip()))
- if wait:
- errors = self.checkRouterCores(reportOnce=True)
- if self.checkRouterVersion("<", minErrorVersion):
- # ignore errors in old versions
- errors = ""
- if assertOnError and len(errors) > 0:
- assert "Errors found - details follow:" == 0, errors
+ if not wait:
+ return errors
+
+ errors = self.checkRouterCores(reportOnce=True)
+ if self.checkRouterVersion("<", minErrorVersion):
+ # ignore errors in old versions
+ errors = ""
+ if assertOnError and len(errors) > 0:
+ assert "Errors found - details follow:" == 0, errors
return errors
def removeIPs(self):
@@ -1348,20 +1395,7 @@ class Router(Node):
"{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon)
)
if len(corefiles) > 0:
- daemon_path = os.path.join(self.daemondir, daemon)
- backtrace = subprocess.check_output(
- [
- "gdb {} {} --batch -ex bt 2> /dev/null".format(
- daemon_path, corefiles[0]
- )
- ],
- shell=True,
- )
- sys.stderr.write(
- "\n%s: %s crashed. Core file found - Backtrace follows:\n"
- % (self.name, daemon)
- )
- sys.stderr.write("%s" % backtrace)
+ backtrace = gdb_core(self, daemon, corefiles)
traces = (
traces
+ "\n%s: %s crashed. Core file found - Backtrace follows:\n%s"
@@ -1431,20 +1465,7 @@ class Router(Node):
"{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon)
)
if len(corefiles) > 0:
- daemon_path = os.path.join(self.daemondir, daemon)
- backtrace = subprocess.check_output(
- [
- "gdb {} {} --batch -ex bt 2> /dev/null".format(
- daemon_path, corefiles[0]
- )
- ],
- shell=True,
- )
- sys.stderr.write(
- "\n%s: %s crashed. Core file found - Backtrace follows:\n"
- % (self.name, daemon)
- )
- sys.stderr.write("%s\n" % backtrace)
+ gdb_core(self, daemon, corefiles)
else:
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
if os.path.isfile(
diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
index 5ae2399e5c..dd42e326ce 100644
--- a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true
}
@@ -18,6 +21,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -38,6 +44,9 @@
{
"type":"SR (OSPF)",
"outLabel":8300,
+ "outLabelStack":[
+ 8300
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -58,6 +67,9 @@
{
"type":"SR (OSPF)",
"outLabel":8400,
+ "outLabelStack":[
+ 8400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -104,6 +116,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -117,6 +132,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
index aedcc5b8f8..f3462e239e 100644
--- a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":20100,
+ "outLabelStack":[
+ 20100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
@@ -26,6 +29,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.1"
@@ -39,6 +45,9 @@
{
"type":"SR (OSPF)",
"outLabel":10400,
+ "outLabelStack":[
+ 10400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -52,6 +61,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -65,6 +77,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -78,6 +93,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.0.1"
@@ -91,6 +109,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.0.1"
@@ -104,6 +125,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
@@ -117,6 +141,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
diff --git a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
index 71e8366137..3d036801d5 100644
--- a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":8100,
+ "outLabelStack":[
+ 8100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -19,6 +22,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -32,6 +38,9 @@
{
"type":"SR (OSPF)",
"outLabel":8400,
+ "outLabelStack":[
+ 8400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -45,6 +54,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -58,6 +70,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
diff --git a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
index b5767e1d7d..86ad8721f8 100644
--- a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":8100,
+ "outLabelStack":[
+ 8100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -19,6 +22,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -32,6 +38,9 @@
{
"type":"SR (OSPF)",
"outLabel":8300,
+ "outLabelStack":[
+ 8300
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -45,6 +54,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true
}
@@ -57,6 +69,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -70,6 +85,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
diff --git a/tests/topotests/pbr-topo1/r1/pbr-map.json b/tests/topotests/pbr-topo1/r1/pbr-map.json
index f0738dc540..bfa0ecb849 100644
--- a/tests/topotests/pbr-topo1/r1/pbr-map.json
+++ b/tests/topotests/pbr-topo1/r1/pbr-map.json
@@ -62,6 +62,32 @@
},
"matchDst":"dead:beef::\/64",
"matchMark":314159
+ },
+ {
+ "sequenceNumber":15,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"ASAKUSA15",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"dead:beef::/64",
+ "matchDscp":10
+ },
+ {
+ "sequenceNumber":20,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"ASAKUSA20",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"dead:beef::/64",
+ "matchEcn":1
}
]
},
diff --git a/tests/topotests/pbr-topo1/r1/pbrd.conf b/tests/topotests/pbr-topo1/r1/pbrd.conf
index 298cba2860..45cb7656ab 100644
--- a/tests/topotests/pbr-topo1/r1/pbrd.conf
+++ b/tests/topotests/pbr-topo1/r1/pbrd.conf
@@ -73,6 +73,16 @@ pbr-map ASAKUSA seq 10
match mark 314159
set nexthop c0ff:ee::1
!
+pbr-map ASAKUSA seq 15
+ match dst-ip dead:beef::/64
+ match dscp af11
+ set nexthop c0ff:ee::1
+!
+pbr-map ASAKUSA seq 20
+ match dst-ip dead:beef::/64
+ match ecn 1
+ set nexthop c0ff:ee::1
+!
# Interface policies
int r1-eth1
pbr-policy EVA
diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini
index ade5bfd501..77b8c2b478 100644
--- a/tests/topotests/pytest.ini
+++ b/tests/topotests/pytest.ini
@@ -1,6 +1,6 @@
# Skip pytests example directory
[pytest]
-norecursedirs = .git example-test example-topojson-test lib docker
+norecursedirs = .git example-test example-topojson-test lib docker evpn_type5_test_topo1
[topogen]
# Default configuration values
diff --git a/tests/topotests/route-scale/test_route_scale.py b/tests/topotests/route-scale/test_route_scale.py
index 9ba0c7e50e..508d1746b3 100755
--- a/tests/topotests/route-scale/test_route_scale.py
+++ b/tests/topotests/route-scale/test_route_scale.py
@@ -112,99 +112,103 @@ def test_converge_protocols():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
-def test_route_install():
- "Test route install for a variety of ecmp"
+def run_one_setup(r1, s):
+ "Run one ecmp config"
- tgen = get_topogen()
- # Don't run this test if we have any failure.
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
+ # Extract params
+ expected_installed = s['expect_in']
+ expected_removed = s['expect_rem']
- installed_file = "{}/r1/installed.routes.json".format(CWD)
- expected_installed = json.loads(open(installed_file).read())
+ count = s['count']
+ wait = s['wait']
- removed_file = "{}/r1/no.routes.json".format(CWD)
- expected_removed = json.loads(open(removed_file).read())
+ logger.info("Testing 1 million routes X {} ecmp".format(s['ecmp']))
- r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("sharp install route 1.0.0.0 \
+ nexthop-group {} 1000000".format(s['nhg']),
+ isjson=False)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group one 1000000", isjson=False)
test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 1 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 1 ecmp removed")
- logger.info(output)
+ success, result = topotest.run_and_expect(test_func, None, count, wait)
+ assert success, "Route scale test install failed:\n{}".format(result)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group two 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 2 ecmp installed")
+ logger.info("1 million routes X {} ecmp installed".format(s['ecmp']))
logger.info(output)
r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 2 ecmp removed")
- logger.info(output)
+ success, result = topotest.run_and_expect(test_func, None, count, wait)
+ assert success, "Route scale test remove failed:\n{}".format(result)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group four 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 4 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 4 ecmp removed")
+ logger.info("1 million routes x {} ecmp removed".format(
+ s['ecmp']))
logger.info(output)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group eight 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 8 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 8 ecmp removed")
- logger.info(output)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group sixteen 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 16 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 16 ecmp removed")
- logger.info(output)
+def test_route_install():
+ "Test route install for a variety of ecmp"
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group thirtytwo 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 32 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 32 ecmp removed")
- logger.info(output)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ installed_file = "{}/r1/installed.routes.json".format(CWD)
+ expected_installed = json.loads(open(installed_file).read())
+
+ removed_file = "{}/r1/no.routes.json".format(CWD)
+ expected_removed = json.loads(open(removed_file).read())
+
+ # dict keys of params: ecmp number, corresponding nhg name, timeout,
+ # number of times to wait
+ scale_keys = ['ecmp', 'nhg', 'wait', 'count', 'expect_in', 'expect_rem']
+
+ # Table of defaults, used for timeout values and 'expected' objects
+ scale_defaults = dict(zip(scale_keys, [None, None, 7, 30,
+ expected_installed,
+ expected_removed]))
+
+ # List of params for each step in the test; note extra time given
+ # for the highest ecmp steps. Executing 'show' at scale can be costly
+ # so we widen the interval there too.
+ scale_steps = [
+ [1, 'one'], [2, 'two'], [4, 'four'],
+ [8, 'eight'], [16, 'sixteen', 10, 40], [32, 'thirtytwo', 10, 40]
+ ]
+
+ # Build up a list of dicts with params for each step of the test;
+ # use defaults where the step doesn't supply a value
+ scale_setups = []
+ for s in scale_steps:
+ d = dict(zip(scale_keys, s))
+ for k in scale_keys:
+ if k not in d:
+ d[k] = scale_defaults[k]
+
+ scale_setups.append(d)
+
+ # Avoid top ecmp case for runs with < 4G memory
+ p = os.popen('free')
+ l = p.readlines()[1].split()
+ mem = int(l[1])
+ if mem < 4000000:
+ logger.info('Limited memory available: {}, skipping x32 testcase'.format(mem))
+ scale_setups = scale_setups[0:-1]
+
+ # Run each step using the dicts we've built
+ for s in scale_setups:
+ run_one_setup(r1, s)
+
+# Mem leak testcase
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+ tgen.report_memory_leaks()
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
diff --git a/tests/topotests/zebra_netlink/__init__.py b/tests/topotests/zebra_netlink/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/zebra_netlink/__init__.py
diff --git a/tests/topotests/zebra_netlink/r1/sharpd.conf b/tests/topotests/zebra_netlink/r1/sharpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/sharpd.conf
diff --git a/tests/topotests/zebra_netlink/r1/v4_route.json b/tests/topotests/zebra_netlink/r1/v4_route.json
new file mode 100644
index 0000000000..61e9bb240b
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/v4_route.json
@@ -0,0 +1,2802 @@
+{
+ "2.1.3.7\/32":[
+ {
+ "prefix":"2.1.3.7\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.8\/32":[
+ {
+ "prefix":"2.1.3.8\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.9\/32":[
+ {
+ "prefix":"2.1.3.9\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.10\/32":[
+ {
+ "prefix":"2.1.3.10\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.11\/32":[
+ {
+ "prefix":"2.1.3.11\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.12\/32":[
+ {
+ "prefix":"2.1.3.12\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.13\/32":[
+ {
+ "prefix":"2.1.3.13\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.14\/32":[
+ {
+ "prefix":"2.1.3.14\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.15\/32":[
+ {
+ "prefix":"2.1.3.15\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.16\/32":[
+ {
+ "prefix":"2.1.3.16\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.17\/32":[
+ {
+ "prefix":"2.1.3.17\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.18\/32":[
+ {
+ "prefix":"2.1.3.18\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.19\/32":[
+ {
+ "prefix":"2.1.3.19\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.20\/32":[
+ {
+ "prefix":"2.1.3.20\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.21\/32":[
+ {
+ "prefix":"2.1.3.21\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.22\/32":[
+ {
+ "prefix":"2.1.3.22\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.23\/32":[
+ {
+ "prefix":"2.1.3.23\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.24\/32":[
+ {
+ "prefix":"2.1.3.24\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.25\/32":[
+ {
+ "prefix":"2.1.3.25\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.26\/32":[
+ {
+ "prefix":"2.1.3.26\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.27\/32":[
+ {
+ "prefix":"2.1.3.27\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.28\/32":[
+ {
+ "prefix":"2.1.3.28\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.29\/32":[
+ {
+ "prefix":"2.1.3.29\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.30\/32":[
+ {
+ "prefix":"2.1.3.30\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.31\/32":[
+ {
+ "prefix":"2.1.3.31\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.32\/32":[
+ {
+ "prefix":"2.1.3.32\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.33\/32":[
+ {
+ "prefix":"2.1.3.33\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.34\/32":[
+ {
+ "prefix":"2.1.3.34\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.35\/32":[
+ {
+ "prefix":"2.1.3.35\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.36\/32":[
+ {
+ "prefix":"2.1.3.36\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.37\/32":[
+ {
+ "prefix":"2.1.3.37\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.38\/32":[
+ {
+ "prefix":"2.1.3.38\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.39\/32":[
+ {
+ "prefix":"2.1.3.39\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.40\/32":[
+ {
+ "prefix":"2.1.3.40\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.41\/32":[
+ {
+ "prefix":"2.1.3.41\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.42\/32":[
+ {
+ "prefix":"2.1.3.42\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.43\/32":[
+ {
+ "prefix":"2.1.3.43\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.44\/32":[
+ {
+ "prefix":"2.1.3.44\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.45\/32":[
+ {
+ "prefix":"2.1.3.45\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.46\/32":[
+ {
+ "prefix":"2.1.3.46\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.47\/32":[
+ {
+ "prefix":"2.1.3.47\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.48\/32":[
+ {
+ "prefix":"2.1.3.48\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.49\/32":[
+ {
+ "prefix":"2.1.3.49\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.50\/32":[
+ {
+ "prefix":"2.1.3.50\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.51\/32":[
+ {
+ "prefix":"2.1.3.51\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.52\/32":[
+ {
+ "prefix":"2.1.3.52\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.53\/32":[
+ {
+ "prefix":"2.1.3.53\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.54\/32":[
+ {
+ "prefix":"2.1.3.54\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.55\/32":[
+ {
+ "prefix":"2.1.3.55\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.56\/32":[
+ {
+ "prefix":"2.1.3.56\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.57\/32":[
+ {
+ "prefix":"2.1.3.57\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.58\/32":[
+ {
+ "prefix":"2.1.3.58\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.59\/32":[
+ {
+ "prefix":"2.1.3.59\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.60\/32":[
+ {
+ "prefix":"2.1.3.60\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.61\/32":[
+ {
+ "prefix":"2.1.3.61\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.62\/32":[
+ {
+ "prefix":"2.1.3.62\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.63\/32":[
+ {
+ "prefix":"2.1.3.63\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.64\/32":[
+ {
+ "prefix":"2.1.3.64\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.65\/32":[
+ {
+ "prefix":"2.1.3.65\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.66\/32":[
+ {
+ "prefix":"2.1.3.66\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.67\/32":[
+ {
+ "prefix":"2.1.3.67\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.68\/32":[
+ {
+ "prefix":"2.1.3.68\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.69\/32":[
+ {
+ "prefix":"2.1.3.69\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.70\/32":[
+ {
+ "prefix":"2.1.3.70\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.71\/32":[
+ {
+ "prefix":"2.1.3.71\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.72\/32":[
+ {
+ "prefix":"2.1.3.72\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.73\/32":[
+ {
+ "prefix":"2.1.3.73\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.74\/32":[
+ {
+ "prefix":"2.1.3.74\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.75\/32":[
+ {
+ "prefix":"2.1.3.75\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.76\/32":[
+ {
+ "prefix":"2.1.3.76\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.77\/32":[
+ {
+ "prefix":"2.1.3.77\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.78\/32":[
+ {
+ "prefix":"2.1.3.78\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.79\/32":[
+ {
+ "prefix":"2.1.3.79\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.80\/32":[
+ {
+ "prefix":"2.1.3.80\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.81\/32":[
+ {
+ "prefix":"2.1.3.81\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.82\/32":[
+ {
+ "prefix":"2.1.3.82\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.83\/32":[
+ {
+ "prefix":"2.1.3.83\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.84\/32":[
+ {
+ "prefix":"2.1.3.84\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.85\/32":[
+ {
+ "prefix":"2.1.3.85\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.86\/32":[
+ {
+ "prefix":"2.1.3.86\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.87\/32":[
+ {
+ "prefix":"2.1.3.87\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.88\/32":[
+ {
+ "prefix":"2.1.3.88\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.89\/32":[
+ {
+ "prefix":"2.1.3.89\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.90\/32":[
+ {
+ "prefix":"2.1.3.90\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.91\/32":[
+ {
+ "prefix":"2.1.3.91\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.92\/32":[
+ {
+ "prefix":"2.1.3.92\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.93\/32":[
+ {
+ "prefix":"2.1.3.93\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.94\/32":[
+ {
+ "prefix":"2.1.3.94\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.95\/32":[
+ {
+ "prefix":"2.1.3.95\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.96\/32":[
+ {
+ "prefix":"2.1.3.96\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.97\/32":[
+ {
+ "prefix":"2.1.3.97\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.98\/32":[
+ {
+ "prefix":"2.1.3.98\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.99\/32":[
+ {
+ "prefix":"2.1.3.99\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.100\/32":[
+ {
+ "prefix":"2.1.3.100\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.101\/32":[
+ {
+ "prefix":"2.1.3.101\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.102\/32":[
+ {
+ "prefix":"2.1.3.102\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.103\/32":[
+ {
+ "prefix":"2.1.3.103\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.104\/32":[
+ {
+ "prefix":"2.1.3.104\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.105\/32":[
+ {
+ "prefix":"2.1.3.105\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.106\/32":[
+ {
+ "prefix":"2.1.3.106\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_netlink/r1/zebra.conf b/tests/topotests/zebra_netlink/r1/zebra.conf
new file mode 100644
index 0000000000..786be19ad4
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/zebra.conf
@@ -0,0 +1,2 @@
+int r1-eth0
+ ip address 192.168.1.1/24 \ No newline at end of file
diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py
new file mode 100755
index 0000000000..7b692c75ab
--- /dev/null
+++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# test_zebra_netlink.py
+#
+# Copyright (c) 2020 by
+#
+# 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_zebra_netlink.py: Test some basic interactions with kernel using Netlink
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+import platform
+from functools import partial
+
+# 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
+from lib.common_config import shutdown_bringup_interface
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+class ZebraTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ tgen.add_router("r1")
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(ZebraTopo, 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_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_zebra_netlink_batching():
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ logger.info(
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ )
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ ptyest.skip("skipped because of preview test failure")
+ r1 = tgen.gears["r1"]
+
+ # Reduce the size of the buffer to hit the limit.
+ r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256")
+
+ r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100")
+ json_file = "{}/r1/v4_route.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expected,)
+ _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
+ assertmsg = '"r1" JSON output mismatches'
+ assert result is None, assertmsg
+
+ r1.vtysh_cmd("sharp remove routes 2.1.3.7 100")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))